瀏覽代碼

Enabled deepcopy of map and space time datasets.
Fixed some topology builder issues. Implemented a fast sample method
that makes use of the topology builder.


git-svn-id: https://svn.osgeo.org/grass/grass/trunk@57598 15284696-431f-4ddb-bdfa-cd5b030d7da7

Soeren Gebbert 12 年之前
父節點
當前提交
f83f0cd86c

+ 243 - 26
lib/python/temporal/abstract_space_time_dataset.py

@@ -484,6 +484,219 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         """!Sample this space time dataset with the temporal topology
         """!Sample this space time dataset with the temporal topology
            of a second space time dataset
            of a second space time dataset
 
 
+           In case spatial is True, the spatial overlap between
+           temporal related maps is performed. Only
+           temporal related and spatial overlapping maps are returned.
+
+           Return all registered maps as ordered (by start_time) object list.
+           Each list entry is a list of map
+           objects which are potentially located in temporal relation to the
+           actual granule of the second space time dataset.
+
+           Each entry in the object list is a dict. The actual sampler
+           map and its temporal extent (the actual granule) and
+           the list of samples are stored:
+
+           @code
+           list = self.sample_by_dataset(stds=sampler, method=[
+               "during","overlap","contains","equal"])
+           for entry in list:
+               granule = entry["granule"]
+               maplist = entry["samples"]
+               for map in maplist:
+                   map.select()
+                   map.print_info()
+           @endcode
+
+           A valid temporal topology (no overlapping or inclusion allowed)
+           is needed to get correct results in case of gaps in the sample
+           dataset.
+
+           Gaps between maps are identified as unregistered maps with id==None.
+
+           The objects are initialized with their id's' and the spatio-temporal
+           extent (temporal type, start time, end time, west, east, south,
+           north, bottom and top).
+           In case more map information are needed, use the select()
+           method for each listed object.
+
+           @param stds The space time dataset to be used for temporal sampling
+           @param method This option specifies what sample method should be
+                         used. In case the registered maps are of temporal
+                         point type, only the start time is used for sampling.
+                         In case of mixed of interval data the user can chose
+                         between:
+
+                  - Example @code ["start", "during", "equals"] @endcode
+
+                  - start: Select maps of which the start time is
+                    located in the selection granule
+                    @verbatim
+                    map    :        s
+                    granule:  s-----------------e
+
+                    map    :        s--------------------e
+                    granule:  s-----------------e
+
+                    map    :        s--------e
+                    granule:  s-----------------e
+                    @endverbatim
+
+                  - contains: Select maps which are temporal
+                              during the selection granule
+                    @verbatim
+                    map    :     s-----------e
+                    granule:  s-----------------e
+                    @endverbatim
+
+                  - overlap: Select maps which temporal overlap
+                    the selection granule, this includes overlaps and overlapped
+                    @verbatim
+                    map    :     s-----------e
+                    granule:        s-----------------e
+
+                    map    :     s-----------e
+                    granule:  s----------e
+                    @endverbatim
+
+                  - during: Select maps which temporally contains
+                            the selection granule
+                    @verbatim
+                    map    :  s-----------------e
+                    granule:     s-----------e
+                    @endverbatim
+
+                  - equals: Select maps which temporally equal
+                    to the selection granule
+                    @verbatim
+                    map    :  s-----------e
+                    granule:  s-----------e
+                    @endverbatim
+
+                  - follows: Select maps which temporally follow
+                    the selection granule
+                    @verbatim
+                    map    :              s-----------e
+                    granule:  s-----------e
+                    @endverbatim
+
+                  - precedes: Select maps which temporally precedes
+                    the selection granule
+                    @verbatim
+                    map    :  s-----------e
+                    granule:              s-----------e
+                    @endverbatim
+
+                  All these methods can be combined. Method must be of
+                  type tuple including the identification strings.
+
+           @param spatial If set True additional the 2d spatial overlapping
+                          is used for selection -> spatio-temporal relation.
+                          The returned map objects will have temporal and
+                          spatial extents
+           @param dbif The database interface to be used
+
+           @return A list of lists of map objects or None in case nothing was
+                   found None
+        """
+
+        if self.get_temporal_type() != stds.get_temporal_type():
+            core.error(_("The space time datasets must be of "
+                         "the same temporal type"))
+            return None
+
+        if stds.get_map_time() != "interval":
+            core.error(_("The temporal map type of the sample "
+                         "dataset must be interval"))
+            return None
+
+        dbif, connected = init_dbif(dbif)
+        relations = copy.deepcopy(method)
+
+        # Tune the temporal relations
+        if "start" in relations:
+            if "overlapped" not in relations:
+                relations.append("overlapped")
+            if "starts" not in relations:
+                relations.append("starts")
+            if "started" not in relations:
+                relations.append("started")
+            if "finishes" not in relations:
+                relations.append("finishes")
+            if "contains" not in relations:
+                relations.append("contains")
+            if "equals" not in relations:
+                relations.append("equals")
+
+        if "overlap" in relations or "over" in relations:
+            if "overlapped" not in relations:
+                relations.append("overlapped")
+            if "overlaps" not in relations:
+                relations.append("overlaps")
+
+        if "contain" in relations:
+            if "contains" not in relations:
+                relations.append("contains")
+
+        # Remove start, equal, contain and overlap
+        relations = [relation.upper().strip() for relation in relations if relation \
+                    not in ["start", "overlap", "contain"]]
+
+        #print(relations)
+
+        tb = SpatioTemporalTopologyBuilder()
+        if spatial:
+            spatial = "2D"
+        else:
+            spatial = None
+
+        mapsA = self.get_registered_maps_as_objects(dbif=dbif)
+        mapsB = stds.get_registered_maps_as_objects_with_gaps(dbif=dbif)
+        tb.build(mapsB, mapsA, spatial)
+
+        obj_list = []
+        for map in mapsB:
+            result = {}
+            maplist = []
+            # Get map relations
+            map_relations = map.get_temporal_relations()
+            #print(map.get_temporal_extent_as_tuple())
+            #for key in map_relations.keys():
+            #    if key not in ["NEXT", "PREV"]:
+            #        print(key, map_relations[key][0].get_temporal_extent_as_tuple())
+
+            result["granule"] = map
+            # Append the maps that fullfill the relations
+            for relation in relations:
+                if relation in map_relations.keys():
+                    for sample_map in map_relations[relation]:
+                        if sample_map not in maplist:
+                            maplist.append(sample_map)
+
+            # Add an empty map if no map was found
+            if not maplist:
+                empty_map = self.get_new_map_instance(None)
+                empty_map.set_spatial_extent(map.get_spatial_extent())
+                empty_map.set_temporal_extent(map.get_temporal_extent())
+                maplist.append(empty_map)
+
+            result["samples"] = maplist
+
+            obj_list.append(result)
+
+        if connected:
+            dbif.close()
+
+        return obj_list
+
+
+    def sample_by_dataset_sql(self, stds, method=None, spatial=False, dbif=None):
+        """!Sample this space time dataset with the temporal topology
+           of a second space time dataset using SQL queries.
+
+           This function is very slow for huge large space time datasets
+           but can run several times in the same process without problems.
+
            The sample dataset must have "interval" as temporal map type,
            The sample dataset must have "interval" as temporal map type,
            so all sample maps have valid interval time.
            so all sample maps have valid interval time.
 
 
@@ -529,6 +742,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                          point type, only the start time is used for sampling.
                          point type, only the start time is used for sampling.
                          In case of mixed of interval data the user can chose
                          In case of mixed of interval data the user can chose
                          between:
                          between:
+                  - ["start", "during", "equals"]
 
 
                   - start: Select maps of which the start time is
                   - start: Select maps of which the start time is
                     located in the selection granule
                     located in the selection granule
@@ -543,15 +757,15 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                     granule:  s-----------------e
                     granule:  s-----------------e
                     @endverbatim
                     @endverbatim
 
 
-                  - during: Select maps which are temporal
-                    during the selection granule
+                  - contains: Select maps which are temporal
+                              during the selection granule
                     @verbatim
                     @verbatim
                     map    :     s-----------e
                     map    :     s-----------e
                     granule:  s-----------------e
                     granule:  s-----------------e
                     @endverbatim
                     @endverbatim
 
 
                   - overlap: Select maps which temporal overlap
                   - overlap: Select maps which temporal overlap
-                    the selection granule
+                    the selection granule, this includes overlaps and overlapped
                     @verbatim
                     @verbatim
                     map    :     s-----------e
                     map    :     s-----------e
                     granule:        s-----------------e
                     granule:        s-----------------e
@@ -560,14 +774,14 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                     granule:  s----------e
                     granule:  s----------e
                     @endverbatim
                     @endverbatim
 
 
-                  - contain: Select maps which temporally contain
-                    the selection granule
+                  - during: Select maps which temporally contains
+                            the selection granule
                     @verbatim
                     @verbatim
                     map    :  s-----------------e
                     map    :  s-----------------e
                     granule:     s-----------e
                     granule:     s-----------e
                     @endverbatim
                     @endverbatim
 
 
-                  - equal: Select maps which temporally equal
+                  - equals: Select maps which temporally equal
                     to the selection granule
                     to the selection granule
                     @verbatim
                     @verbatim
                     map    :  s-----------e
                     map    :  s-----------e
@@ -591,7 +805,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                   All these methods can be combined. Method must be of
                   All these methods can be combined. Method must be of
                   type tuple including the identification strings.
                   type tuple including the identification strings.
 
 
-           @param spatial If set True additional the spatial overlapping
+           @param spatial If set True additional the 2d spatial overlapping
                           is used for selection -> spatio-temporal relation.
                           is used for selection -> spatio-temporal relation.
                           The returned map objects will have temporal and
                           The returned map objects will have temporal and
                           spatial extents
                           spatial extents
@@ -618,9 +832,9 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                     use_during = True
                     use_during = True
                 if name == "overlap":
                 if name == "overlap":
                     use_overlap = True
                     use_overlap = True
-                if name == "contain":
+                if name == "contain" or name == "contains":
                     use_contain = True
                     use_contain = True
-                if name == "equal":
+                if name == "equal" or name == "equals":
                     use_equal = True
                     use_equal = True
                 if name == "follows":
                 if name == "follows":
                     use_follows = True
                     use_follows = True
@@ -916,6 +1130,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                 next = start + gran
                 next = start + gran
 
 
             map = first.get_new_instance(None)
             map = first.get_new_instance(None)
+            map.set_spatial_extent_from_values(0,0,0,0,0,0)
             if first.is_time_absolute():
             if first.is_time_absolute():
                 map.set_absolute_time(start, next, None)
                 map.set_absolute_time(start, next, None)
             else:
             else:
@@ -1009,6 +1224,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                         elif self.is_time_relative():
                         elif self.is_time_relative():
                             map.set_relative_time(start, end,
                             map.set_relative_time(start, end,
                                                  self.get_relative_time_unit())
                                                  self.get_relative_time_unit())
+                        map.set_spatial_extent_from_values(0,0,0,0,0,0)
                         obj_list.append(copy.copy(map))
                         obj_list.append(copy.copy(map))
 
 
         if connected:
         if connected:
@@ -1076,23 +1292,24 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         # Older temporal databases have no bottom and top columns
         # Older temporal databases have no bottom and top columns
         # in their views so we need a work around to set the full
         # in their views so we need a work around to set the full
         # spatial extent as well
         # spatial extent as well
-        has_bt_columns = True
-        try:
-            rows = self.get_registered_maps(
-                "id,start_time,end_time, west,east,south,north,bottom,top",
-                where, order, dbif)
-        except:
-            try:
-                dbif.rollback()
-                rows = self.get_registered_maps("id,start_time,end_time",
-                                                where, order, dbif)
-                has_bt_columns = False
-                core.warning(_("Old temporal database format detected. "
-                               "The top and "
-                               "bottom column is missing in the views, using"
-                               "a work around."))
-            except:
-                raise
+
+        rows = get_tgis_metadata(dbif)
+        db_version = 0
+
+        if rows:
+            for row in rows:
+                if row["key"] == "tgis_db_version":
+                    db_version = int(row["value"])
+
+        if db_version >= 1:
+            has_bt_columns = True
+            columns = "id,start_time,end_time, west,east,south,north,bottom,top"
+        else:
+            has_bt_columns = False
+            columns = "id,start_time,end_time, west,east,south,north"
+
+        rows = self.get_registered_maps(columns, where, order, dbif)
+
 
 
         if rows is not None:
         if rows is not None:
             for row in rows:
             for row in rows:

+ 83 - 82
lib/python/temporal/base.py

@@ -5,8 +5,8 @@
 Temporal GIS base classes to be used in other
 Temporal GIS base classes to be used in other
 Python temporal gis packages.
 Python temporal gis packages.
 
 
-This packages includes all base classes to store basic information 
-like id, name, mapset creation and modification time as well as sql 
+This packages includes all base classes to store basic information
+like id, name, mapset creation and modification time as well as sql
 serialization and de-serialization and the sql database interface.
 serialization and de-serialization and the sql database interface.
 
 
 Usage:
 Usage:
@@ -41,15 +41,14 @@ from core import *
 class DictSQLSerializer(object):
 class DictSQLSerializer(object):
     def __init__(self):
     def __init__(self):
         self.D = {}
         self.D = {}
-        self.dbif =  SQLDatabaseInterfaceConnection()
 
 
     def serialize(self, type, table, where=None):
     def serialize(self, type, table, where=None):
-        """!Convert the internal dictionary into a string of semicolon 
-            separated SQL statements The keys are the column names and 
+        """!Convert the internal dictionary into a string of semicolon
+            separated SQL statements The keys are the column names and
             the values are the row entries
             the values are the row entries
-            
+
             Usage:
             Usage:
-            
+
             \code
             \code
             >>> init()
             >>> init()
             >>> t = DictSQLSerializer()
             >>> t = DictSQLSerializer()
@@ -67,15 +66,17 @@ class DictSQLSerializer(object):
             ('UPDATE raster_base SET  name = ?  ,creator = ?  ,creation_time = ?  ,modification_time = ?  ,mapset = ?  ,id = ? ;\\n', ('soil', 'soeren', datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2001, 1, 1, 0, 0), 'PERMANENT', 'soil@PERMANENT'))
             ('UPDATE raster_base SET  name = ?  ,creator = ?  ,creation_time = ?  ,modification_time = ?  ,mapset = ?  ,id = ? ;\\n', ('soil', 'soeren', datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2001, 1, 1, 0, 0), 'PERMANENT', 'soil@PERMANENT'))
             >>> t.serialize(type="UPDATE ALL", table="raster_base")
             >>> t.serialize(type="UPDATE ALL", table="raster_base")
             ('UPDATE raster_base SET  name = ?  ,creator = ?  ,creation_time = ?  ,modification_time = ?  ,mapset = ?  ,id = ? ;\\n', ('soil', 'soeren', datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2001, 1, 1, 0, 0), 'PERMANENT', 'soil@PERMANENT'))
             ('UPDATE raster_base SET  name = ?  ,creator = ?  ,creation_time = ?  ,modification_time = ?  ,mapset = ?  ,id = ? ;\\n', ('soil', 'soeren', datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2001, 1, 1, 0, 0), 'PERMANENT', 'soil@PERMANENT'))
-            
+
             @param type must be SELECT. INSERT, UPDATE
             @param type must be SELECT. INSERT, UPDATE
             @param table The name of the table to select, insert or update
             @param table The name of the table to select, insert or update
             @param where The optional where statement
             @param where The optional where statement
             @return a tuple containing the SQL string and the arguments
             @return a tuple containing the SQL string and the arguments
-            
+
             \endcode
             \endcode
         """
         """
 
 
+        dbif =  SQLDatabaseInterfaceConnection()
+
         sql = ""
         sql = ""
         args = []
         args = []
 
 
@@ -109,12 +110,12 @@ class DictSQLSerializer(object):
             sql += ') VALUES ('
             sql += ') VALUES ('
             for key in self.D.keys():
             for key in self.D.keys():
                 if count == 0:
                 if count == 0:
-                    if self.dbif.dbmi.paramstyle == "qmark":
+                    if dbif.dbmi.paramstyle == "qmark":
                         sql += '?'
                         sql += '?'
                     else:
                     else:
                         sql += '%s'
                         sql += '%s'
                 else:
                 else:
-                    if self.dbif.dbmi.paramstyle == "qmark":
+                    if dbif.dbmi.paramstyle == "qmark":
                         sql += ' ,?'
                         sql += ' ,?'
                     else:
                     else:
                         sql += ' ,%s'
                         sql += ' ,%s'
@@ -134,13 +135,13 @@ class DictSQLSerializer(object):
                 # Update only entries which are not None
                 # Update only entries which are not None
                 if self.D[key] is not None:
                 if self.D[key] is not None:
                     if count == 0:
                     if count == 0:
-                        if self.dbif.dbmi.paramstyle == "qmark":
+                        if dbif.dbmi.paramstyle == "qmark":
                             sql += ' %s = ? ' % key
                             sql += ' %s = ? ' % key
                         else:
                         else:
                             sql += ' %s ' % key
                             sql += ' %s ' % key
                             sql += '= %s '
                             sql += '= %s '
                     else:
                     else:
-                        if self.dbif.dbmi.paramstyle == "qmark":
+                        if dbif.dbmi.paramstyle == "qmark":
                             sql += ' ,%s = ? ' % key
                             sql += ' ,%s = ? ' % key
                         else:
                         else:
                             sql += ' ,%s ' % key
                             sql += ' ,%s ' % key
@@ -157,13 +158,13 @@ class DictSQLSerializer(object):
             sql += 'UPDATE ' + table + ' SET '
             sql += 'UPDATE ' + table + ' SET '
             for key in self.D.keys():
             for key in self.D.keys():
                 if count == 0:
                 if count == 0:
-                    if self.dbif.dbmi.paramstyle == "qmark":
+                    if dbif.dbmi.paramstyle == "qmark":
                         sql += ' %s = ? ' % key
                         sql += ' %s = ? ' % key
                     else:
                     else:
                         sql += ' %s ' % key
                         sql += ' %s ' % key
                         sql += '= %s '
                         sql += '= %s '
                 else:
                 else:
-                    if self.dbif.dbmi.paramstyle == "qmark":
+                    if dbif.dbmi.paramstyle == "qmark":
                         sql += ' ,%s = ? ' % key
                         sql += ' ,%s = ? ' % key
                     else:
                     else:
                         sql += ' ,%s ' % key
                         sql += ' ,%s ' % key
@@ -177,7 +178,7 @@ class DictSQLSerializer(object):
         return sql, tuple(args)
         return sql, tuple(args)
 
 
     def deserialize(self, row):
     def deserialize(self, row):
-        """!Convert the content of the dbmi dictionary like row into the 
+        """!Convert the content of the dbmi dictionary like row into the
            internal dictionary
            internal dictionary
 
 
            @param row The dictionary like row to store in the internal dict
            @param row The dictionary like row to store in the internal dict
@@ -200,19 +201,19 @@ class DictSQLSerializer(object):
 class SQLDatabaseInterface(DictSQLSerializer):
 class SQLDatabaseInterface(DictSQLSerializer):
     """!This class represents the SQL database interface
     """!This class represents the SQL database interface
 
 
-       Functions to insert, select and update the internal 
+       Functions to insert, select and update the internal
        structure of this class in the temporal database are implemented.
        structure of this class in the temporal database are implemented.
-       This is the base class for raster, raster3d, vector and 
+       This is the base class for raster, raster3d, vector and
        space time datasets data management classes:
        space time datasets data management classes:
        - Identification information (base)
        - Identification information (base)
        - Spatial extent
        - Spatial extent
        - Temporal extent
        - Temporal extent
        - Metadata
        - Metadata
-       
+
        Usage:
        Usage:
-       
+
        \code
        \code
-       
+
         >>> init()
         >>> init()
         >>> t = SQLDatabaseInterface("raster", "soil@PERMANENT")
         >>> t = SQLDatabaseInterface("raster", "soil@PERMANENT")
         >>> t.D["name"] = "soil"
         >>> t.D["name"] = "soil"
@@ -239,14 +240,14 @@ class SQLDatabaseInterface(DictSQLSerializer):
         ("UPDATE raster SET  creation_time = ?  ,mapset = ?  ,name = ?  ,creator = ? WHERE id = 'soil@PERMANENT';\\n", (datetime.datetime(2001, 1, 1, 0, 0), 'PERMANENT', 'soil', 'soeren'))
         ("UPDATE raster SET  creation_time = ?  ,mapset = ?  ,name = ?  ,creator = ? WHERE id = 'soil@PERMANENT';\\n", (datetime.datetime(2001, 1, 1, 0, 0), 'PERMANENT', 'soil', 'soeren'))
         >>> t.get_update_all_statement_mogrified()
         >>> t.get_update_all_statement_mogrified()
         "UPDATE raster SET  creation_time = '2001-01-01 00:00:00'  ,mapset = 'PERMANENT'  ,name = 'soil'  ,creator = 'soeren' WHERE id = 'soil@PERMANENT';\\n"
         "UPDATE raster SET  creation_time = '2001-01-01 00:00:00'  ,mapset = 'PERMANENT'  ,name = 'soil'  ,creator = 'soeren' WHERE id = 'soil@PERMANENT';\\n"
-        
+
         \endcode
         \endcode
     """
     """
     def __init__(self, table=None, ident=None):
     def __init__(self, table=None, ident=None):
         """!Constructor of this class
         """!Constructor of this class
 
 
            @param table The name of the table
            @param table The name of the table
-           @param ident The identifier (primary key) of this 
+           @param ident The identifier (primary key) of this
                          object in the database table
                          object in the database table
         """
         """
         DictSQLSerializer.__init__(self)
         DictSQLSerializer.__init__(self)
@@ -255,7 +256,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         self.ident = ident
         self.ident = ident
 
 
     def get_table_name(self):
     def get_table_name(self):
-        """!Return the name of the table in which the internal 
+        """!Return the name of the table in which the internal
            data are inserted, updated or selected
            data are inserted, updated or selected
            @return The name of the table
            @return The name of the table
            """
            """
@@ -271,7 +272,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
     def delete(self, dbif=None):
     def delete(self, dbif=None):
         """!Delete the entry of this object from the temporal database
         """!Delete the entry of this object from the temporal database
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
         """
         """
         sql = self.get_delete_statement()
         sql = self.get_delete_statement()
@@ -296,7 +297,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
     def is_in_db(self, dbif=None):
     def is_in_db(self, dbif=None):
         """!Check if this object is present in the temporal database
         """!Check if this object is present in the temporal database
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @return True if this object is present in the temporal database, False otherwise
            @return True if this object is present in the temporal database, False otherwise
         """
         """
@@ -321,17 +322,17 @@ class SQLDatabaseInterface(DictSQLSerializer):
         return True
         return True
 
 
     def get_select_statement(self):
     def get_select_statement(self):
-        """!Return the sql statement and the argument list in 
+        """!Return the sql statement and the argument list in
            database specific style
            database specific style
            @return The SELECT string
            @return The SELECT string
         """
         """
-        return self.serialize("SELECT", self.get_table_name(), 
+        return self.serialize("SELECT", self.get_table_name(),
                               "WHERE id = \'" + str(self.ident) + "\'")
                               "WHERE id = \'" + str(self.ident) + "\'")
 
 
     def get_select_statement_mogrified(self, dbif=None):
     def get_select_statement_mogrified(self, dbif=None):
         """!Return the select statement as mogrified string
         """!Return the select statement as mogrified string
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @return The SELECT string
            @return The SELECT string
         """
         """
@@ -344,7 +345,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         """!Select the content from the temporal database and store it
         """!Select the content from the temporal database and store it
            in the internal dictionary structure
            in the internal dictionary structure
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
         """
         """
         sql, args = self.get_select_statement()
         sql, args = self.get_select_statement()
@@ -380,7 +381,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         return True
         return True
 
 
     def get_insert_statement(self):
     def get_insert_statement(self):
-        """!Return the sql statement and the argument 
+        """!Return the sql statement and the argument
            list in database specific style
            list in database specific style
            @return The INSERT string"""
            @return The INSERT string"""
         return self.serialize("INSERT", self.get_table_name())
         return self.serialize("INSERT", self.get_table_name())
@@ -388,7 +389,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
     def get_insert_statement_mogrified(self, dbif=None):
     def get_insert_statement_mogrified(self, dbif=None):
         """!Return the insert statement as mogrified string
         """!Return the insert statement as mogrified string
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @return The INSERT string
            @return The INSERT string
         """
         """
@@ -401,7 +402,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         """!Serialize the content of this object and store it in the temporal
         """!Serialize the content of this object and store it in the temporal
            database using the internal identifier
            database using the internal identifier
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
         """
         """
         sql, args = self.get_insert_statement()
         sql, args = self.get_insert_statement()
@@ -417,24 +418,24 @@ class SQLDatabaseInterface(DictSQLSerializer):
             dbif.close()
             dbif.close()
 
 
     def get_update_statement(self, ident=None):
     def get_update_statement(self, ident=None):
-        """!Return the sql statement and the argument list 
+        """!Return the sql statement and the argument list
            in database specific style
            in database specific style
-           
+
            @param ident The identifier to be updated, useful for renaming
            @param ident The identifier to be updated, useful for renaming
            @return The UPDATE string
            @return The UPDATE string
-           
+
            """
            """
         if ident:
         if ident:
-            return self.serialize("UPDATE", self.get_table_name(), 
+            return self.serialize("UPDATE", self.get_table_name(),
                               "WHERE id = \'" + str(ident) + "\'")
                               "WHERE id = \'" + str(ident) + "\'")
         else:
         else:
-            return self.serialize("UPDATE", self.get_table_name(), 
+            return self.serialize("UPDATE", self.get_table_name(),
                               "WHERE id = \'" + str(self.ident) + "\'")
                               "WHERE id = \'" + str(self.ident) + "\'")
 
 
     def get_update_statement_mogrified(self, dbif=None, ident=None):
     def get_update_statement_mogrified(self, dbif=None, ident=None):
         """!Return the update statement as mogrified string
         """!Return the update statement as mogrified string
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @param ident The identifier to be updated, useful for renaming
            @param ident The identifier to be updated, useful for renaming
            @return The UPDATE string
            @return The UPDATE string
@@ -450,7 +451,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
 
 
            Only object entries which are exists (not None) are updated
            Only object entries which are exists (not None) are updated
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @param ident The identifier to be updated, useful for renaming
            @param ident The identifier to be updated, useful for renaming
         """
         """
@@ -470,23 +471,23 @@ class SQLDatabaseInterface(DictSQLSerializer):
             dbif.close()
             dbif.close()
 
 
     def get_update_all_statement(self, ident=None):
     def get_update_all_statement(self, ident=None):
-        """!Return the sql statement and the argument 
+        """!Return the sql statement and the argument
            list in database specific style
            list in database specific style
-           
+
            @param ident The identifier to be updated, useful for renaming
            @param ident The identifier to be updated, useful for renaming
            @return The UPDATE string
            @return The UPDATE string
            """
            """
         if ident:
         if ident:
-            return self.serialize("UPDATE ALL", self.get_table_name(), 
+            return self.serialize("UPDATE ALL", self.get_table_name(),
                               "WHERE id = \'" + str(ident) + "\'")
                               "WHERE id = \'" + str(ident) + "\'")
         else:
         else:
-            return self.serialize("UPDATE ALL", self.get_table_name(), 
+            return self.serialize("UPDATE ALL", self.get_table_name(),
                               "WHERE id = \'" + str(self.ident) + "\'")
                               "WHERE id = \'" + str(self.ident) + "\'")
 
 
     def get_update_all_statement_mogrified(self, dbif=None, ident=None):
     def get_update_all_statement_mogrified(self, dbif=None, ident=None):
         """!Return the update all statement as mogrified string
         """!Return the update all statement as mogrified string
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @param ident The identifier to be updated, useful for renaming
            @param ident The identifier to be updated, useful for renaming
            @return The UPDATE string
            @return The UPDATE string
@@ -497,10 +498,10 @@ class SQLDatabaseInterface(DictSQLSerializer):
         return dbif.mogrify_sql_statement(self.get_update_all_statement(ident))
         return dbif.mogrify_sql_statement(self.get_update_all_statement(ident))
 
 
     def update_all(self, dbif=None, ident=None):
     def update_all(self, dbif=None, ident=None):
-        """!Serialize the content of this object, including None objects, 
+        """!Serialize the content of this object, including None objects,
         and update it in the temporal database using the internal identifier
         and update it in the temporal database using the internal identifier
 
 
-           @param dbif The database interface to be used, 
+           @param dbif The database interface to be used,
                         if None a temporary connection will be established
                         if None a temporary connection will be established
            @param ident The identifier to be updated, useful for renaming
            @param ident The identifier to be updated, useful for renaming
         """
         """
@@ -523,11 +524,11 @@ class SQLDatabaseInterface(DictSQLSerializer):
 
 
 
 
 class DatasetBase(SQLDatabaseInterface):
 class DatasetBase(SQLDatabaseInterface):
-    """!This is the base class for all maps and spacetime datasets storing 
+    """!This is the base class for all maps and spacetime datasets storing
         basic identification information
         basic identification information
-        
+
         Usage:
         Usage:
-        
+
         \code
         \code
 
 
         >>> init()
         >>> init()
@@ -559,22 +560,22 @@ class DatasetBase(SQLDatabaseInterface):
         creator=soeren
         creator=soeren
         creation_time=2001-01-01 00:00:00
         creation_time=2001-01-01 00:00:00
         temporal_type=absolute
         temporal_type=absolute
-        
+
         \endcode
         \endcode
     """
     """
-    
-    def __init__(self, table=None, ident=None, name=None, mapset=None, 
+
+    def __init__(self, table=None, ident=None, name=None, mapset=None,
                  creator=None, ctime=None,ttype=None):
                  creator=None, ctime=None,ttype=None):
         """!Constructor
         """!Constructor
-        
-            @param table The name of the temporal database table 
+
+            @param table The name of the temporal database table
                           that should be used to store the values
                           that should be used to store the values
-            @param ident The unique identifier must be a combination of 
-                          the dataset name, layer name and the mapset 
+            @param ident The unique identifier must be a combination of
+                          the dataset name, layer name and the mapset
                           "name@mapset" or "name:layer@mapset"
                           "name@mapset" or "name:layer@mapset"
                           used as as primary key in the temporal database
                           used as as primary key in the temporal database
             @param name The name of the map or dataset
             @param name The name of the map or dataset
-            @param mapset The name of the mapset 
+            @param mapset The name of the mapset
             @param creator The name of the creator
             @param creator The name of the creator
             @param ctime The creation datetime object
             @param ctime The creation datetime object
             @param ttype The temporal type
             @param ttype The temporal type
@@ -602,8 +603,8 @@ class DatasetBase(SQLDatabaseInterface):
     def set_id(self, ident):
     def set_id(self, ident):
         """!Convenient method to set the unique identifier (primary key)
         """!Convenient method to set the unique identifier (primary key)
 
 
-           @param ident The unique identifier must be a combination 
-                         of the dataset name, layer name and the mapset 
+           @param ident The unique identifier must be a combination
+                         of the dataset name, layer name and the mapset
                          "name@mapset" or "name:layer@mapset"
                          "name@mapset" or "name:layer@mapset"
         """
         """
         self.ident = ident
         self.ident = ident
@@ -653,7 +654,7 @@ class DatasetBase(SQLDatabaseInterface):
         self.D["creator"] = creator
         self.D["creator"] = creator
 
 
     def set_ctime(self, ctime=None):
     def set_ctime(self, ctime=None):
-        """!Set the creation time of the dataset, 
+        """!Set the creation time of the dataset,
            if nothing set the current time is used
            if nothing set the current time is used
 
 
            @param ctime The current time of type datetime
            @param ctime The current time of type datetime
@@ -664,7 +665,7 @@ class DatasetBase(SQLDatabaseInterface):
             self.D["creation_time"] = ctime
             self.D["creation_time"] = ctime
 
 
     def set_ttype(self, ttype):
     def set_ttype(self, ttype):
-        """!Set the temporal type of the dataset: absolute or relative, 
+        """!Set the temporal type of the dataset: absolute or relative,
            if nothing set absolute time will assumed
            if nothing set absolute time will assumed
 
 
            @param ttype The temporal type of the dataset "absolute or relative"
            @param ttype The temporal type of the dataset "absolute or relative"
@@ -696,7 +697,7 @@ class DatasetBase(SQLDatabaseInterface):
             return None
             return None
 
 
     def get_map_id(self):
     def get_map_id(self):
-        """!Convenient method to get the unique map identifier 
+        """!Convenient method to get the unique map identifier
            without layer information
            without layer information
 
 
            @return the name of the vector map as "name@mapset"
            @return the name of the vector map as "name@mapset"
@@ -822,24 +823,24 @@ class DatasetBase(SQLDatabaseInterface):
 
 
 class RasterBase(DatasetBase):
 class RasterBase(DatasetBase):
     """!Time stamped raster map base information class"""
     """!Time stamped raster map base information class"""
-    def __init__(self, ident=None, name=None, mapset=None, creator=None, 
+    def __init__(self, ident=None, name=None, mapset=None, creator=None,
                  creation_time=None, temporal_type=None):
                  creation_time=None, temporal_type=None):
-        DatasetBase.__init__(self, "raster_base", ident, name, mapset, 
+        DatasetBase.__init__(self, "raster_base", ident, name, mapset,
                               creator, creation_time, temporal_type)
                               creator, creation_time, temporal_type)
 
 
 
 
 class Raster3DBase(DatasetBase):
 class Raster3DBase(DatasetBase):
     """!Time stamped 3D raster map base information class"""
     """!Time stamped 3D raster map base information class"""
-    def __init__(self, ident=None, name=None, mapset=None, creator=None, 
+    def __init__(self, ident=None, name=None, mapset=None, creator=None,
                  creation_time=None, temporal_type=None,):
                  creation_time=None, temporal_type=None,):
-        DatasetBase.__init__(self, "raster3d_base", ident, name, 
-                              mapset, creator, creation_time, 
+        DatasetBase.__init__(self, "raster3d_base", ident, name,
+                              mapset, creator, creation_time,
                               temporal_type)
                               temporal_type)
 
 
 
 
 class VectorBase(DatasetBase):
 class VectorBase(DatasetBase):
     """!Time stamped vector map base information class"""
     """!Time stamped vector map base information class"""
-    def __init__(self, ident=None, name=None, mapset=None, layer=None, 
+    def __init__(self, ident=None, name=None, mapset=None, layer=None,
                  creator=None, creation_time=None, temporal_type=None):
                  creator=None, creation_time=None, temporal_type=None):
         DatasetBase.__init__(self, "vector_base", ident, name, mapset,
         DatasetBase.__init__(self, "vector_base", ident, name, mapset,
                               creator, creation_time, temporal_type)
                               creator, creation_time, temporal_type)
@@ -861,14 +862,14 @@ class VectorBase(DatasetBase):
 
 
 class STDSBase(DatasetBase):
 class STDSBase(DatasetBase):
     """!Base class for space time datasets
     """!Base class for space time datasets
-    
-       This class adds the semantic type member variable to the dataset 
+
+       This class adds the semantic type member variable to the dataset
        base class.
        base class.
-       
+
     Usage:
     Usage:
 
 
     \code
     \code
-    
+
     >>> init()
     >>> init()
     >>> t = STDSBase("stds", "soil@PERMANENT", semantic_type="average", creator="soeren", ctime=datetime(2001,1,1), ttype="absolute")
     >>> t = STDSBase("stds", "soil@PERMANENT", semantic_type="average", creator="soeren", ctime=datetime(2001,1,1), ttype="absolute")
     >>> t.semantic_type
     >>> t.semantic_type
@@ -890,10 +891,10 @@ class STDSBase(DatasetBase):
     creation_time=2001-01-01 00:00:00
     creation_time=2001-01-01 00:00:00
     temporal_type=absolute
     temporal_type=absolute
     semantic_type=average
     semantic_type=average
-    
+
     \endcode
     \endcode
     """
     """
-    def __init__(self, table=None, ident=None, name=None, mapset=None, 
+    def __init__(self, table=None, ident=None, name=None, mapset=None,
                  semantic_type=None, creator=None, ctime=None,
                  semantic_type=None, creator=None, ctime=None,
                  ttype=None):
                  ttype=None):
         DatasetBase.__init__(self, table, ident, name, mapset, creator,
         DatasetBase.__init__(self, table, ident, name, mapset, creator,
@@ -914,7 +915,7 @@ class STDSBase(DatasetBase):
             return None
             return None
 
 
     semantic_type = property(fget=get_semantic_type, fset=set_semantic_type)
     semantic_type = property(fget=get_semantic_type, fset=set_semantic_type)
-    
+
     def print_info(self):
     def print_info(self):
         """!Print information about this class in human readable style"""
         """!Print information about this class in human readable style"""
         DatasetBase.print_info(self)
         DatasetBase.print_info(self)
@@ -932,30 +933,30 @@ class STDSBase(DatasetBase):
 
 
 class STRDSBase(STDSBase):
 class STRDSBase(STDSBase):
     """!Space time raster dataset base information class"""
     """!Space time raster dataset base information class"""
-    def __init__(self, ident=None, name=None, mapset=None, 
+    def __init__(self, ident=None, name=None, mapset=None,
                  semantic_type=None, creator=None, ctime=None,
                  semantic_type=None, creator=None, ctime=None,
                  ttype=None):
                  ttype=None):
-        STDSBase.__init__(self, "strds_base", ident, name, mapset, 
+        STDSBase.__init__(self, "strds_base", ident, name, mapset,
                            semantic_type, creator, ctime,
                            semantic_type, creator, ctime,
                            ttype)
                            ttype)
 
 
 
 
 class STR3DSBase(STDSBase):
 class STR3DSBase(STDSBase):
     """!Space time 3D raster dataset base information class"""
     """!Space time 3D raster dataset base information class"""
-    def __init__(self, ident=None, name=None, mapset=None, 
+    def __init__(self, ident=None, name=None, mapset=None,
                  semantic_type=None, creator=None, ctime=None,
                  semantic_type=None, creator=None, ctime=None,
                  ttype=None):
                  ttype=None):
-        STDSBase.__init__(self, "str3ds_base", ident, name, mapset, 
+        STDSBase.__init__(self, "str3ds_base", ident, name, mapset,
                            semantic_type, creator, ctime,
                            semantic_type, creator, ctime,
                            ttype)
                            ttype)
 
 
 
 
 class STVDSBase(STDSBase):
 class STVDSBase(STDSBase):
     """!Space time vector dataset base information class"""
     """!Space time vector dataset base information class"""
-    def __init__(self, ident=None, name=None, mapset=None, 
+    def __init__(self, ident=None, name=None, mapset=None,
                  semantic_type=None, creator=None, ctime=None,
                  semantic_type=None, creator=None, ctime=None,
                  ttype=None):
                  ttype=None):
-        STDSBase.__init__(self, "stvds_base", ident, name, mapset, 
+        STDSBase.__init__(self, "stvds_base", ident, name, mapset,
                            semantic_type, creator, ctime,
                            semantic_type, creator, ctime,
                            ttype)
                            ttype)
 
 

+ 1 - 0
lib/python/temporal/core.py

@@ -408,6 +408,7 @@ def _create_tgis_metadata_table(content, dbif=None):
 
 
 class SQLDatabaseInterfaceConnection():
 class SQLDatabaseInterfaceConnection():
     """!This class represents the database interface connection
     """!This class represents the database interface connection
+       and provides access to the chisen backend modules.
 
 
        The following DBMS are supported:
        The following DBMS are supported:
          - sqlite via the sqlite3 standard library
          - sqlite via the sqlite3 standard library

+ 0 - 1
lib/python/temporal/datetime_math.py

@@ -20,7 +20,6 @@ SECOND_AS_DAY = 1.1574074074074073e-05
 
 
 ###############################################################################
 ###############################################################################
 
 
-
 def relative_time_to_time_delta(value):
 def relative_time_to_time_delta(value):
     """!Convert the double value representing days
     """!Convert the double value representing days
        into a timedelta object.
        into a timedelta object.

+ 13 - 14
lib/python/temporal/sampling.py

@@ -25,19 +25,18 @@ for details.
 from space_time_datasets import *
 from space_time_datasets import *
 from factory import *
 from factory import *
 
 
-###############################################################################
 
 
-def sample_stds_by_stds_topology(intype, sampletype, inputs, sampler, header, 
-                                 separator, method, spatial=False, 
+def sample_stds_by_stds_topology(intype, sampletype, inputs, sampler, header,
+                                 separator, method, spatial=False,
                                  print_only=True):
                                  print_only=True):
-    """!Sample the input space time datasets with a sample 
-       space time dataset, return the created map matrix and optionally 
+    """!Sample the input space time datasets with a sample
+       space time dataset, return the created map matrix and optionally
        print the result to stdout
        print the result to stdout
 
 
-        In case multiple maps are located in the current granule, 
+        In case multiple maps are located in the current granule,
         the map names are separated by comma.
         the map names are separated by comma.
 
 
-        In case a layer is present, the names map ids are extended 
+        In case a layer is present, the names map ids are extended
         in this form: "name:layer@mapset"
         in this form: "name:layer@mapset"
 
 
         Attention: Do not use the comma as separator for printing
         Attention: Do not use the comma as separator for printing
@@ -48,13 +47,13 @@ def sample_stds_by_stds_topology(intype, sampletype, inputs, sampler, header,
         @param sampler Name of a space time dataset used for temporal sampling
         @param sampler Name of a space time dataset used for temporal sampling
         @param header Set True to print column names
         @param header Set True to print column names
         @param separator The field separator character between the columns
         @param separator The field separator character between the columns
-        @param method The method to be used for temporal sampling 
+        @param method The method to be used for temporal sampling
                        (start,during,contain,overlap,equal)
                        (start,during,contain,overlap,equal)
         @param spatial Perform spatial overlapping check
         @param spatial Perform spatial overlapping check
-        @param print_only If set True (default) then the result of the sampling will be 
-                    printed to stdout, if set to False the resulting map matrix 
-                    will be returned. 
-                    
+        @param print_only If set True (default) then the result of the sampling will be
+                    printed to stdout, if set to False the resulting map matrix
+                    will be returned.
+
         @return The map matrix or None if nothing found
         @return The map matrix or None if nothing found
     """
     """
     mapset = core.gisenv()["MAPSET"]
     mapset = core.gisenv()["MAPSET"]
@@ -105,7 +104,7 @@ def sample_stds_by_stds_topology(intype, sampletype, inputs, sampler, header,
             mapmatrizes.append(mapmatrix)
             mapmatrizes.append(mapmatrix)
 
 
     if len(mapmatrizes) > 0:
     if len(mapmatrizes) > 0:
-        
+
         # Simply return the map matrix
         # Simply return the map matrix
         if not print_only:
         if not print_only:
             dbif.close()
             dbif.close()
@@ -166,5 +165,5 @@ def sample_stds_by_stds_topology(intype, sampletype, inputs, sampler, header,
     dbif.close()
     dbif.close()
     if len(mapmatrizes) > 0:
     if len(mapmatrizes) > 0:
         return mapmatrizes
         return mapmatrizes
-    
+
     return None
     return None

+ 61 - 24
lib/python/temporal/spatio_temporal_relationships.py

@@ -320,6 +320,42 @@ class SpatioTemporalTopologyBuilder(object):
         ('OVERLAPPED', (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4)))
         ('OVERLAPPED', (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4)))
         ('EQUAL', (datetime.datetime(2000, 1, 1, 0, 0, 3), datetime.datetime(2000, 1, 1, 0, 0, 5)))
         ('EQUAL', (datetime.datetime(2000, 1, 1, 0, 0, 3), datetime.datetime(2000, 1, 1, 0, 0, 5)))
 
 
+        >>> mapsA = []
+        >>> for i in range(4):
+        ...     idA = "a%i@B"%(i)
+        ...     mapA = tgis.RasterDataset(idA)
+        ...     start = datetime.datetime(2000, 1, 1, 0, 0, i)
+        ...     end = datetime.datetime(2000, 1, 1, 0, 0, i + 2)
+        ...     check = mapA.set_absolute_time(start, end)
+        ...     mapsA.append(mapA)
+        >>> tb = SpatioTemporalTopologyBuilder()
+        >>> tb.build(mapsA)
+        >>> # Check relations of mapsA
+        >>> for map in mapsA:
+        ...     print(map.get_temporal_extent_as_tuple())
+        ...     m = map.get_temporal_relations()
+        ...     for key in m.keys():
+        ...         if key not in ["NEXT", "PREV"]:
+        ...             print(key, m[key][0].get_temporal_extent_as_tuple())
+        (datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(2000, 1, 1, 0, 0, 2))
+        ('OVERLAPS', (datetime.datetime(2000, 1, 1, 0, 0, 1), datetime.datetime(2000, 1, 1, 0, 0, 3)))
+        ('PRECEDES', (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4)))
+        ('EQUAL', (datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(2000, 1, 1, 0, 0, 2)))
+        (datetime.datetime(2000, 1, 1, 0, 0, 1), datetime.datetime(2000, 1, 1, 0, 0, 3))
+        ('OVERLAPS', (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4)))
+        ('OVERLAPPED', (datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(2000, 1, 1, 0, 0, 2)))
+        ('PRECEDES', (datetime.datetime(2000, 1, 1, 0, 0, 3), datetime.datetime(2000, 1, 1, 0, 0, 5)))
+        ('EQUAL', (datetime.datetime(2000, 1, 1, 0, 0, 1), datetime.datetime(2000, 1, 1, 0, 0, 3)))
+        (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4))
+        ('OVERLAPS', (datetime.datetime(2000, 1, 1, 0, 0, 3), datetime.datetime(2000, 1, 1, 0, 0, 5)))
+        ('OVERLAPPED', (datetime.datetime(2000, 1, 1, 0, 0, 1), datetime.datetime(2000, 1, 1, 0, 0, 3)))
+        ('FOLLOWS', (datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(2000, 1, 1, 0, 0, 2)))
+        ('EQUAL', (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4)))
+        (datetime.datetime(2000, 1, 1, 0, 0, 3), datetime.datetime(2000, 1, 1, 0, 0, 5))
+        ('FOLLOWS', (datetime.datetime(2000, 1, 1, 0, 0, 1), datetime.datetime(2000, 1, 1, 0, 0, 3)))
+        ('OVERLAPPED', (datetime.datetime(2000, 1, 1, 0, 0, 2), datetime.datetime(2000, 1, 1, 0, 0, 4)))
+        ('EQUAL', (datetime.datetime(2000, 1, 1, 0, 0, 3), datetime.datetime(2000, 1, 1, 0, 0, 5)))
+
         @endcode
         @endcode
 
 
     """
     """
@@ -494,7 +530,7 @@ class SpatioTemporalTopologyBuilder(object):
 
 
         if mapsB == None:
         if mapsB == None:
             mapsB = mapsA
             mapsB = mapsA
-            idetnical = True
+            identical = True
 
 
         for map_ in mapsA:
         for map_ in mapsA:
             map_.reset_topology()
             map_.reset_topology()
@@ -505,15 +541,16 @@ class SpatioTemporalTopologyBuilder(object):
 
 
         tree = self. _build_rtree(mapsA, spatial)
         tree = self. _build_rtree(mapsA, spatial)
 
 
+        list_ = gis.G_new_ilist()
+
         for j in xrange(len(mapsB)):
         for j in xrange(len(mapsB)):
 
 
-            list_ = gis.ilist()
             rect = self._map_to_rect(tree, mapsB[j], spatial)
             rect = self._map_to_rect(tree, mapsB[j], spatial)
-            vector.RTreeSearch2(tree, rect, byref(list_))
+            vector.RTreeSearch2(tree, rect, list_)
             vector.RTreeFreeRect(rect)
             vector.RTreeFreeRect(rect)
 
 
-            for k in xrange(list_.n_values):
-                i = list_.value[k] - 1
+            for k in xrange(list_.contents.n_values):
+                i = list_.contents.value[k] - 1
 
 
                 # Get the temporal relationship
                 # Get the temporal relationship
                 relation = mapsB[j].temporal_relation(mapsA[i])
                 relation = mapsB[j].temporal_relation(mapsA[i])
@@ -530,6 +567,8 @@ class SpatioTemporalTopologyBuilder(object):
         if not identical and mapsB != None:
         if not identical and mapsB != None:
             self._build_iteratable(mapsB, spatial)
             self._build_iteratable(mapsB, spatial)
 
 
+        gis.G_free_ilist(list_)
+
         vector.RTreeDestroyTree(tree)
         vector.RTreeDestroyTree(tree)
 
 
     def __iter__(self):
     def __iter__(self):
@@ -550,16 +589,15 @@ class SpatioTemporalTopologyBuilder(object):
 ###############################################################################
 ###############################################################################
 
 
 def set_temoral_relationship(A, B, relation):
 def set_temoral_relationship(A, B, relation):
-    if relation == "equal":
-        if B != A:
-            if not B.get_equal() or \
-            (B.get_equal() and \
-            A not in B.get_equal()):
-                B.append_equal(A)
-            if not A.get_equal() or \
-            (A.get_equal() and \
-            B not in A.get_equal()):
-                A.append_equal(B)
+    if relation == "equal" or relation == "equals":
+        if not B.get_equal() or \
+        (B.get_equal() and \
+        A not in B.get_equal()):
+            B.append_equal(A)
+        if not A.get_equal() or \
+        (A.get_equal() and \
+        B not in A.get_equal()):
+            A.append_equal(B)
     elif relation == "follows":
     elif relation == "follows":
         if not B.get_follows() or \
         if not B.get_follows() or \
             (B.get_follows() and \
             (B.get_follows() and \
@@ -658,15 +696,14 @@ def set_temoral_relationship(A, B, relation):
 def set_spatial_relationship(A, B, relation):
 def set_spatial_relationship(A, B, relation):
 
 
     if relation == "equivalent":
     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)
+        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":
     elif relation == "overlap":
         if not B.get_overlap() or \
         if not B.get_overlap() or \
             (B.get_overlap() and \
             (B.get_overlap() and \