浏览代码

Modifications are:
- Removed the creation of a register table for each registered map, which speeds things up massively
- Added modification time to space time datasets
- Added aggregation type to space time datasets that will be set by t.rast.aggregate
- Keeping the SQL scripts as simple as possible
- Removed foreign primary keys and created indexes for primary keys in sqlite3 (postgreql does this automagically)
- Using delete trigger instead of foreign primary keys in postgresql backend
- Removed unneeded SQL scripts
- Using Python dateutil module for time string parsing if installed, or a much more simpler parser without time zone support
- Added GRASS environmental variables to allow registration of maps from different mapsets in a space time dataset
- Simplified code for map registration
- Code cleanup ...



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

Soeren Gebbert 11 年之前
父节点
当前提交
d94771e8db
共有 52 个文件被更改,包括 1220 次插入1676 次删除
  1. 25 9
      lib/python/temporal/abstract_dataset.py
  2. 152 94
      lib/python/temporal/abstract_map_dataset.py
  3. 185 207
      lib/python/temporal/abstract_space_time_dataset.py
  4. 123 43
      lib/python/temporal/base.py
  5. 163 51
      lib/python/temporal/core.py
  6. 58 8
      lib/python/temporal/datetime_math.py
  7. 1 6
      lib/python/temporal/extract.py
  8. 9 9
      lib/python/temporal/mapcalc.py
  9. 29 68
      lib/python/temporal/metadata.py
  10. 4 3
      lib/python/temporal/register.py
  11. 51 42
      lib/python/temporal/space_time_datasets.py
  12. 18 47
      lib/python/temporal/temporal_extent.py
  13. 10 8
      lib/python/temporal/unittests_register.py
  14. 0 12
      lib/temporal/SQL/map_stds_register_table_template.sql
  15. 13 15
      lib/temporal/SQL/map_tables_template.sql
  16. 77 0
      lib/temporal/SQL/postgresql_delete_trigger.sql
  17. 14 0
      lib/temporal/SQL/postgresql_indexes.sql
  18. 4 5
      lib/temporal/SQL/raster3d_metadata_table.sql
  19. 15 11
      lib/temporal/SQL/raster3d_views.sql
  20. 3 4
      lib/temporal/SQL/raster_metadata_table.sql
  21. 14 10
      lib/temporal/SQL/raster_views.sql
  22. 10 0
      lib/temporal/SQL/sqlite3_delete_trigger.sql
  23. 51 0
      lib/temporal/SQL/sqlite3_indexes.sql
  24. 2 6
      lib/temporal/SQL/stds_map_register_table_template.sql
  25. 0 39
      lib/temporal/SQL/stds_raster3d_register_trigger_template.sql
  26. 0 51
      lib/temporal/SQL/stds_raster_register_trigger_template.sql
  27. 10 9
      lib/temporal/SQL/stds_tables_template.sql
  28. 0 3
      lib/temporal/SQL/stds_vector_register_trigger_template.sql
  29. 5 4
      lib/temporal/SQL/str3ds_metadata_table.sql
  30. 7 6
      lib/temporal/SQL/str3ds_views.sql
  31. 5 3
      lib/temporal/SQL/strds_metadata_table.sql
  32. 7 6
      lib/temporal/SQL/strds_views.sql
  33. 4 5
      lib/temporal/SQL/stvds_metadata_table.sql
  34. 9 7
      lib/temporal/SQL/stvds_views.sql
  35. 0 836
      lib/temporal/SQL/test.temporal.py
  36. 1 0
      lib/temporal/SQL/update_stds_spatial_temporal_extent_template.sql
  37. 1 0
      lib/temporal/SQL/update_str3ds_metadata_template.sql
  38. 4 4
      lib/temporal/SQL/vector_metadata_table.sql
  39. 16 10
      lib/temporal/SQL/vector_views.sql
  40. 33 10
      temporal/benchmark.sh
  41. 3 1
      temporal/t.rast.aggregate.ds/t.rast.aggregate.ds.py
  42. 3 1
      temporal/t.rast.aggregate/t.rast.aggregate.py
  43. 3 3
      temporal/t.rast.gapfill/t.rast.gapfill.py
  44. 1 1
      temporal/t.rast.import/test.t.rast.import.relative.sh
  45. 1 10
      temporal/t.rast.series/t.rast.series.py
  46. 1 0
      temporal/t.rast3d.univar/test.t.rast3d.univar.sh
  47. 3 1
      temporal/t.register/t.register.py
  48. 60 0
      temporal/t.register/test.t.register.raster.file.timezone.sh
  49. 3 1
      temporal/t.remove/t.remove.py
  50. 4 4
      temporal/t.support/t.support.py
  51. 2 1
      temporal/t.support/test.t.support.sh
  52. 3 2
      temporal/t.unregister/t.unregister.py

+ 25 - 9
lib/python/temporal/abstract_dataset.py

@@ -148,6 +148,13 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
         """
 
     @abstractmethod
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+
+    @abstractmethod
     def get_type(self):
         """!Return the type of this class as string
 
@@ -224,6 +231,8 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
         self.temporal_extent.set_id(ident)
         self.spatial_extent.set_id(ident)
         self.metadata.set_id(ident)
+        if self.is_stds() is False:
+            self.stds_register.set_id(ident)
 
     def get_id(self):
         """!Return the unique identifier of the dataset
@@ -257,23 +266,20 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
 
     def get_absolute_time(self):
         """!Returns the start time, the end
-           time and the timezone of the map as tuple
-
-           @attention: The timezone is currently not used.
+           time of the map as tuple
 
            The start time is of type datetime.
 
            The end time is of type datetime in case of interval time,
            or None on case of a time instance.
 
-           @return A tuple of (start_time, end_time, timezone)
+           @return A tuple of (start_time, end_time)
         """
 
         start = self.absolute_time.get_start_time()
         end = self.absolute_time.get_end_time()
-        tz = self.absolute_time.get_timezone()
 
-        return (start, end, tz)
+        return (start, end)
 
     def get_relative_time(self):
         """!Returns the start time, the end
@@ -353,6 +359,8 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
         self.temporal_extent.select(dbif)
         self.spatial_extent.select(dbif)
         self.metadata.select(dbif)
+        if self.is_stds() is False:
+            self.stds_register.select(dbif)
 
         if connected:
             dbif.close()
@@ -379,7 +387,7 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
             @return The SQL insert statement in case execute=False, or an empty string otherwise
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to insert dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -391,6 +399,8 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
         statement += self.temporal_extent.get_insert_statement_mogrified(dbif)
         statement += self.spatial_extent.get_insert_statement_mogrified(dbif)
         statement += self.metadata.get_insert_statement_mogrified(dbif)
+        if self.is_stds() is False:
+            statement += self.stds_register.get_insert_statement_mogrified(dbif)
 
         if execute:
             dbif.execute_transaction(statement)
@@ -414,7 +424,7 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
            @return The SQL update statement in case execute=False, or an empty string otherwise
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to update dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                                  {"ds":self.get_id(), "type":self.get_type()})
@@ -430,6 +440,9 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
                                                                         ident)
         statement += self.metadata.get_update_statement_mogrified(dbif, ident)
 
+        if self.is_stds() is False:
+            statement += self.stds_register.get_update_statement_mogrified(dbif, ident)
+
         if execute:
             dbif.execute_transaction(statement)
             if connected:
@@ -452,7 +465,7 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
            @return The SQL update statement in case execute=False, or an empty string otherwise
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to update dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -467,6 +480,9 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
             dbif, ident)
         statement += self.metadata.get_update_all_statement_mogrified(dbif, ident)
 
+        if self.is_stds() is False:
+            statement += self.stds_register.get_update_all_statement_mogrified(dbif, ident)
+
         if execute:
             dbif.execute_transaction(statement)
             if connected:

+ 152 - 94
lib/python/temporal/abstract_map_dataset.py

@@ -15,7 +15,6 @@ for details.
 from abstract_dataset import *
 from datetime_math import *
 
-
 class AbstractMapDataset(AbstractDataset):
     """!This is the base class for all maps (raster, vector, raster3d).
 
@@ -52,27 +51,6 @@ class AbstractMapDataset(AbstractDataset):
            @return The new space time dataset instance
         """
 
-    @abstractmethod
-    def get_stds_register(self):
-        """!Return the space time dataset register table name
-
-            Maps can be registered in several different space time datasets.
-            This method returns the name of the register table in the
-            temporal database.
-
-            @return The name of the stds register table
-        """
-
-    @abstractmethod
-    def set_stds_register(self, name):
-        """!Set the space time dataset register table name.
-
-           This table stores all space time datasets in
-           which this map is registered.
-
-           @param name The name of the register table
-        """
-
     def check_resolution_with_current_region(self):
         """!Check if the raster or voxel resolution is
            finer than the current resolution
@@ -141,7 +119,7 @@ class AbstractMapDataset(AbstractDataset):
         start = ""
 
         if self.is_time_absolute():
-            start_time, end_time, tz = self.get_absolute_time()
+            start_time, end_time = self.get_absolute_time()
             start = datetime_to_grass_datetime_string(start_time)
             if end_time is not None:
                 end = datetime_to_grass_datetime_string(end_time)
@@ -212,6 +190,7 @@ class AbstractMapDataset(AbstractDataset):
         self.temporal_extent.print_self()
         self.spatial_extent.print_self()
         self.metadata.print_self()
+        self.stds_register.print_self()
 
     def print_info(self):
         """!Print information about this object in human readable style"""
@@ -244,9 +223,9 @@ class AbstractMapDataset(AbstractDataset):
                     string += "\n | ............................ "
                     count = 0
                 if count == 0:
-                    string += ds["id"]
+                    string += ds
                 else:
-                    string += ",%s" % ds["id"]
+                    string += ",%s" % ds
                 count += 1
         print " | Registered datasets ........ " + string
         print " +----------------------------------------------------------------------------+"
@@ -263,9 +242,9 @@ class AbstractMapDataset(AbstractDataset):
         if datasets is not None:
             for ds in datasets:
                 if count == 0:
-                    string += ds["id"]
+                    string += ds
                 else:
-                    string += ",%s" % ds["id"]
+                    string += ",%s" % ds
                 count += 1
             print "registered_datasets=" + string
 
@@ -278,7 +257,9 @@ class AbstractMapDataset(AbstractDataset):
 
            This functions assures that the timestamp is written to the
            grass file system based database in addition to the temporal
-           database entry.
+           database entry. The stds register table will be created as well.
+           Hence maps can only be registered in a space time dataset, when
+           they were inserted in the temporal database beforehand.
 
            @param dbif The database interface to be used
            @param execute If True the SQL statements will be executed.
@@ -287,8 +268,9 @@ class AbstractMapDataset(AbstractDataset):
            @return The SQL insert statement in case execute=False, or an
                    empty string otherwise
         """
-        self.write_timestamp_to_grass()
-        return AbstractDataset.insert(self, dbif, execute)
+        if get_enable_timestamp_write():
+            self.write_timestamp_to_grass()
+        return AbstractDataset.insert(self, dbif=dbif, execute=execute)
 
     def update(self, dbif=None, execute=True):
         """!Update the map content in the database from the internal structure
@@ -305,7 +287,8 @@ class AbstractMapDataset(AbstractDataset):
            @return The SQL insert statement in case execute=False, or an
                    empty string otherwise
         """
-        self.write_timestamp_to_grass()
+        if get_enable_timestamp_write():
+            self.write_timestamp_to_grass()
         return AbstractDataset.update(self, dbif, execute)
 
     def update_all(self, dbif=None, execute=True):
@@ -323,7 +306,8 @@ class AbstractMapDataset(AbstractDataset):
             @return The SQL insert statement in case execute=False, or an
                     empty string otherwise
         """
-        self.write_timestamp_to_grass()
+        if get_enable_timestamp_write():
+            self.write_timestamp_to_grass()
         return AbstractDataset.update_all(self, dbif, execute)
 
     def set_time_to_absolute(self):
@@ -334,7 +318,7 @@ class AbstractMapDataset(AbstractDataset):
         """!Set the temporal type to relative"""
         self.base.set_ttype("relative")
 
-    def set_absolute_time(self, start_time, end_time=None, timezone=None):
+    def set_absolute_time(self, start_time, end_time=None):
         """!Set the absolute time with start time and end time
 
             The end time is optional and must be set to None in case of time
@@ -347,7 +331,6 @@ class AbstractMapDataset(AbstractDataset):
                              map
            @param end_time a datetime object specifying the end time of the
                            map, None in case or time instance
-           @param timezone Thee timezone of the map (not used)
 
            @return True for success and False otherwise
         """
@@ -401,12 +384,10 @@ class AbstractMapDataset(AbstractDataset):
         self.base.set_ttype("absolute")
         self.absolute_time.set_start_time(start_time)
         self.absolute_time.set_end_time(end_time)
-        self.absolute_time.set_timezone(timezone)
 
         return True
 
-    def update_absolute_time(self, start_time, end_time=None,
-                             timezone=None, dbif=None):
+    def update_absolute_time(self, start_time, end_time=None, dbif=None):
         """!Update the absolute time
 
            The end time is optional and must be set to None in case of time
@@ -420,17 +401,16 @@ class AbstractMapDataset(AbstractDataset):
                   the map
            @param end_time a datetime object specifying the end time of the
                   map, None in case or time instance
-           @param timezone Thee timezone of the map (not used)
            @param dbif The database interface to be used
            """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to update dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
 
 
-        if self.set_absolute_time(start_time, end_time, timezone):
+        if self.set_absolute_time(start_time, end_time):
             dbif, connected = init_dbif(dbif)
             self.absolute_time.update_all(dbif)
             self.base.update(dbif)
@@ -438,7 +418,8 @@ class AbstractMapDataset(AbstractDataset):
             if connected:
                 dbif.close()
 
-            self.write_timestamp_to_grass()
+            if get_enable_timestamp_write():
+                self.write_timestamp_to_grass()
 
     def set_relative_time(self, start_time, end_time, unit):
         """!Set the relative time interval
@@ -514,7 +495,7 @@ class AbstractMapDataset(AbstractDataset):
            @param unit The relative time unit
            @param dbif The database interface to be used
         """
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to update dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -527,7 +508,8 @@ class AbstractMapDataset(AbstractDataset):
             if connected:
                 dbif.close()
 
-            self.write_timestamp_to_grass()
+            if get_enable_timestamp_write():
+                self.write_timestamp_to_grass()
 
     def set_temporal_extent(self, extent):
         """!Convenient method to set the temporal extent from a temporal extent object
@@ -550,11 +532,11 @@ class AbstractMapDataset(AbstractDataset):
            (datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(2001, 1, 1, 0, 0))
 
            >>> map1 = tgis.VectorDataset("A@P")
-           >>> check = map1.set_absolute_time(datetime.datetime(2000,5,5), datetime.datetime(2005,6,6), None)
+           >>> check = map1.set_absolute_time(datetime.datetime(2000,5,5), datetime.datetime(2005,6,6))
            >>> print map1.get_temporal_extent_as_tuple()
            (datetime.datetime(2000, 5, 5, 0, 0), datetime.datetime(2005, 6, 6, 0, 0))
            >>> map2 = tgis.RasterDataset("B@P")
-           >>> check = map2.set_absolute_time(datetime.datetime(1990,1,1), datetime.datetime(1999,8,1), None)
+           >>> check = map2.set_absolute_time(datetime.datetime(1990,1,1), datetime.datetime(1999,8,1))
            >>> print map2.get_temporal_extent_as_tuple()
            (datetime.datetime(1990, 1, 1, 0, 0), datetime.datetime(1999, 8, 1, 0, 0))
            >>> map2.set_temporal_extent(map1.get_temporal_extent())
@@ -573,9 +555,8 @@ class AbstractMapDataset(AbstractDataset):
         elif issubclass(type(extent), AbsoluteTemporalExtent):
             start = extent.get_start_time()
             end = extent.get_end_time()
-            tz = extent.get_timezone()
 
-            self.set_absolute_time(start, end, tz)
+            self.set_absolute_time(start, end)
 
     def temporal_buffer(self, increment, update=False, dbif=None):
         """!Create a temporal buffer based on an increment
@@ -655,7 +636,7 @@ class AbstractMapDataset(AbstractDataset):
         """
 
         if self.is_time_absolute():
-            start, end, tz = self.get_absolute_time()
+            start, end = self.get_absolute_time()
 
             new_start = decrement_datetime_by_string(start, increment)
             if end == None:
@@ -664,9 +645,9 @@ class AbstractMapDataset(AbstractDataset):
                 new_end = increment_datetime_by_string(end, increment)
 
             if update:
-                self.update_absolute_time(new_start, new_end, tz, dbif=dbif)
+                self.update_absolute_time(new_start, new_end, dbif=dbif)
             else:
-                self.set_absolute_time(new_start, new_end, tz)
+                self.set_absolute_time(new_start, new_end)
         else:
             start, end, unit = self.get_relative_time()
             new_start = start - increment
@@ -774,7 +755,7 @@ class AbstractMapDataset(AbstractDataset):
     def check_for_correct_time(self):
         """!Check for correct time"""
         if self.is_time_absolute():
-            start, end, tz = self.get_absolute_time()
+            start, end= self.get_absolute_time()
         else:
             start, end, unit = self.get_relative_time()
 
@@ -820,7 +801,7 @@ class AbstractMapDataset(AbstractDataset):
            @return The SQL statements if execute=False, else an empty string,
                    None in case of a failure
         """
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to delete dataset <%(ds)s> of type %(type)s from the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -837,11 +818,6 @@ class AbstractMapDataset(AbstractDataset):
             statement += self.unregister(
                 dbif=dbif, update=update, execute=False)
 
-            # Remove the strds register table
-            if self.get_stds_register() is not None:
-                statement += "DROP TABLE IF EXISTS " + self.get_stds_register() + ";\n"
-
-
             self.msgr.verbose(_("Delete %s dataset <%s> from temporal database")
                          % (self.get_type(), self.get_id()))
 
@@ -851,6 +827,7 @@ class AbstractMapDataset(AbstractDataset):
 
         if execute:
             dbif.execute_transaction(statement)
+            statement = ""
 
         # Remove the timestamp from the file system
         self.remove_timestamp_from_grass()
@@ -860,9 +837,6 @@ class AbstractMapDataset(AbstractDataset):
         if connected:
             dbif.close()
 
-        if execute:
-            return ""
-
         return statement
 
     def unregister(self, dbif=None, update=True, execute=True):
@@ -883,16 +857,16 @@ class AbstractMapDataset(AbstractDataset):
 
 
         if self.get_layer() is not None:
-            self.msgr.debug(1, _("Unregister %(type)s map <%(map)s> with "
+            self.msgr.debug(1, "Unregister %(type)s map <%(map)s> with "
                            "layer %(layer)s from space time datasets" % \
                          {'type':self.get_type(), 'map':self.get_map_id(),
-                          'layer':self.get_layer()}))
+                          'layer':self.get_layer()})
         else:
-            self.msgr.debug(1, _("Unregister %(type)s map <%(map)s> "
+            self.msgr.debug(1, "Unregister %(type)s map <%(map)s> "
                            "from space time datasets"
-                         % {'type':self.get_type(), 'map':self.get_map_id()}))
+                         % {'type':self.get_type(), 'map':self.get_map_id()})
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to unregister dataset <%(ds)s> of type %(type)s from the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -901,59 +875,143 @@ class AbstractMapDataset(AbstractDataset):
         dbif, connected = init_dbif(dbif)
 
         # Get all datasets in which this map is registered
-        rows = self.get_registered_datasets(dbif)
+        datasets = self.get_registered_datasets(dbif)
 
         # For each stds in which the map is registered
-        if rows is not None:
-            for row in rows:
-                # Create a space time dataset object to remove the map
-                # from its register
-                stds = self.get_new_stds_instance(row["id"])
-                stds.metadata.select(dbif)
-                statement += stds.unregister_map(self, dbif, False)
-                # Take care to update the space time dataset after
-                # the map has been unregistered
-                if update == True and execute == True:
-                    stds.update_from_registered_maps(dbif)
+        if datasets is not None:
+            for dataset in datasets:
+                    # Create a space time dataset object to remove the map
+                    # from its register
+                    stds = self.get_new_stds_instance(dataset)
+                    stds.metadata.select(dbif)
+                    statement += stds.unregister_map(self, dbif, False)
+                    # Take care to update the space time dataset after
+                    # the map has been unregistered
+                    if update == True and execute == True:
+                        stds.update_from_registered_maps(dbif)
 
         if execute:
             dbif.execute_transaction(statement)
+            statement = ""
 
         if connected:
             dbif.close()
 
-        if execute:
-            return ""
-
         return statement
 
     def get_registered_datasets(self, dbif=None):
         """!Return all space time dataset ids in which this map is registered
-           as dictionary like rows with column "id" or None if this map is not
+           as as a list of strings, or None if this map is not
            registered in any space time dataset.
 
            @param dbif The database interface to be used
-           @return The SQL rows with the ids of all space time datasets in
+           @return A list of ids of all space time datasets in
                    which this map is registered
         """
         dbif, connected = init_dbif(dbif)
 
-        rows = None
+        self.stds_register.select(dbif)
+        datasets = self.stds_register.get_registered_stds()
 
-        try:
-            if self.get_stds_register() is not None:
-                # Select all stds tables in which this map is registered
-                sql = "SELECT id FROM " + self.get_stds_register()
-                dbif.cursor.execute(sql)
-                rows = dbif.cursor.fetchall()
-        except:
-            self.msgr.error(_("Unable to select space time dataset register table "
-                         "<%s>") % (self.get_stds_register()))
+        if datasets is not None and datasets != "" and datasets.find("@") >= 0:
+            datasets = datasets.split(",")
+        else:
+            datasets = None
 
         if connected:
-            dbif.close()
+            dbif.close
+
+        return datasets
+
+    def add_dataset_to_register(self, stds_id, dbif=None, execute=True):
+        """!Add a new space time dataset to the register
+
+           @param stds_id The id of the space time dataset to be registered
+           @param dbif The database interface to be used
+           @param execute If True the SQL INSERT table statements
+                          will be executed.
+                          If False the prepared SQL statements are
+                          returned and must be executed by the caller.
+
+           @return The SQL statements if execute=False, else an empty string
+        """
+        dbif, connected = init_dbif(dbif=dbif)
+
+        datasets = self.get_registered_datasets(dbif=dbif)
+
+        if stds_id is None or stds_id == "":
+            return ""
 
-        return rows
+        # Check if no datasets are present
+        if datasets is None:
+            datasets = []
+
+        # Check if the dataset is already present
+        if stds_id in datasets:
+            if connected:
+                dbif.close
+            return ""
+
+        datasets.append(stds_id)
+
+        self.stds_register.set_registered_stds(",".join(datasets))
+
+        statement = ""
+
+        if execute is True:
+            self.stds_register.update(dbif=dbif)
+        else:
+            statement = self.stds_register.get_update_statement_mogrified(dbif=dbif)
+
+        if connected:
+            dbif.close
+
+        return statement
+
+
+    def remove_dataset_from_register(self, stds_id, dbif=None, execute=True):
+        """!Remove a space time dataset from the register
+
+           @param stds_id The id of the space time dataset to removed from the registered
+           @param dbif The database interface to be used
+           @param execute If True the SQL INSERT table statements
+                          will be executed.
+                          If False the prepared SQL statements are
+                          returned and must be executed by the caller.
+
+           @return The SQL statements if execute=False, else an empty string
+        """
+        dbif, connected = init_dbif(dbif)
+
+        datasets = self.get_registered_datasets(dbif=dbif)
+
+        # Check if no datasets are present
+        if datasets is None:
+            if connected:
+                dbif.close
+            return ""
+
+        # Check if the dataset is already present
+        if stds_id not in datasets:
+            if connected:
+                dbif.close
+            return ""
+
+        datasets.remove(stds_id)
+
+        self.stds_register.set_registered_stds(",".join(datasets))
+
+        statement = ""
+
+        if execute is True:
+            self.stds_register.update(dbif=dbif)
+        else:
+            statement = self.stds_register.get_update_statement_mogrified(dbif=dbif)
+
+        if connected:
+            dbif.close
+
+        return statement
 
 ###############################################################################
 

+ 185 - 207
lib/python/temporal/abstract_space_time_dataset.py

@@ -125,7 +125,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         """
         self.metadata.print_history()
 
-    def set_initial_values(self, temporal_type, semantic_type,
+    def set_initial_values(self, temporal_type, semantic_type=None,
                            title=None, description=None):
         """!Set the initial values of the space time dataset
 
@@ -157,6 +157,13 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         self.metadata.set_description(description)
         self.metadata.set_command(self.create_command_string())
 
+    def set_aggregation_type(self, aggregation_type):
+        """!Set the aggregation type of the space time dataset
+
+           @param aggregation_type The aggregation type of the space time dataset
+        """
+        self.metadata.set_aggregation_type(aggregation_type)
+
     def update_command_string(self, dbif=None):
         """!Append the current command string to any existing command string
            in the metadata class and calls metadata update
@@ -296,6 +303,70 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                 self.msgr.fatal(_("Unsupported temporal unit: %s") % (unit))
             self.relative_time.set_unit(unit)
 
+    def insert(self, dbif=None, execute=True):
+        """!Insert the space time dataset content into the database from the internal
+           structure
+
+           The map register table will be created, so that maps
+           can be registered.
+
+           @param dbif The database interface to be used
+           @param execute If True the SQL statements will be executed.
+                          If False the prepared SQL statements are
+                          returned and must be executed by the caller.
+           @return The SQL insert statement in case execute=False, or an
+                   empty string otherwise
+        """
+
+        dbif, connected = init_dbif(dbif)
+
+        # We need to create the register table if it does not exists
+        stds_register_table = self.get_map_register()
+
+        # Create the map register table
+        sql_path = get_sql_template_path()
+        statement = ""
+
+        # We need to create the map register table
+        if stds_register_table is None:
+            # Create table name
+            stds_register_table = self.create_map_register_name()
+            # Assure that the table and index do not exist
+            #dbif.execute_transaction("DROP INDEX IF EXISTS %s; DROP TABLE IF EXISTS   %s;"%(stds_register_table + "_index", stds_register_table))
+
+            # Read the SQL template
+            sql = open(os.path.join(sql_path,
+                                    "stds_map_register_table_template.sql"),
+                                    'r').read()
+            map_type = self.get_new_map_instance(None).get_type()
+            stds_name = self.get_name() + "_" + self.get_mapset()
+
+            # Create a raster, raster3d or vector tables
+            sql = sql.replace("GRASS_MAP", map_type)
+            sql = sql.replace("SPACETIME_NAME", stds_name)
+            statement += sql
+
+            if dbif.dbmi.__name__ == "sqlite3":
+                statement += "CREATE INDEX %s_index ON %s (id);"%(stds_register_table, stds_register_table)
+
+            # Set the map register table name
+            self.set_map_register(stds_register_table)
+
+            self.msgr.debug(1, _("Created register table <%s> for space "
+                           "time %s  dataset <%s>") %
+                          (stds_register_table, self.get_new_map_instance(None).get_type(), self.get_id()))
+
+        statement += AbstractDataset.insert(self, dbif=dbif, execute=False)
+
+        if execute:
+            dbif.execute_transaction(statement)
+            statement = ""
+
+        if connected:
+            dbif.close()
+
+        return statement
+
     def get_map_time(self):
         """!Return the type of the map time, interval, point, mixed or invalid
         """
@@ -329,7 +400,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         for i in range(len(maps)):
             # Check for point and interval data
             if maps[i].is_time_absolute():
-                start, end, tz = maps[i].get_absolute_time()
+                start, end = maps[i].get_absolute_time()
             if maps[i].is_time_relative():
                 start, end, unit = maps[i].get_relative_time()
 
@@ -1131,7 +1202,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             map = first.get_new_instance(None)
             map.set_spatial_extent_from_values(0,0,0,0,0,0)
             if first.is_time_absolute():
-                map.set_absolute_time(start, next, None)
+                map.set_absolute_time(start, next)
             else:
                 map.set_relative_time(start, next, first.get_relative_time_unit())
 
@@ -1495,7 +1566,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                    granularity
 
         """
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to shift dataset <%(ds)s> of type %(type)s in the temporal database."
             " The mapset of the dataset does not match the current mapset")%\
             ({"ds":self.get_id()}, {"type":self.get_type()}))
@@ -1662,7 +1733,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to snap dataset <%(ds)s> of type %(type)s in the temporal database."
             " The mapset of the dataset does not match the current mapset")%\
             ({"ds":self.get_id()}, {"type":self.get_type()}))
@@ -1738,7 +1809,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             datasets = map.get_registered_datasets(dbif)
             if datasets:
                 for dataset in datasets:
-                    datatsets_to_modify[dataset["id"]] = dataset["id"]
+                    datatsets_to_modify[dataset] = dataset
 
         self.update_from_registered_maps(dbif)
 
@@ -1762,13 +1833,16 @@ class AbstractSpaceTimeDataset(AbstractDataset):
            @param dbif The database interface to be used
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to rename dataset <%(ds)s> of type %(type)s in the temporal database."
             " The mapset of the dataset does not match the current mapset")%\
             ({"ds":self.get_id()}, {"type":self.get_type()}))
 
         dbif, connected = init_dbif(dbif)
 
+        if dbif.dbmi.__name__ != "sqlite3":
+            self.msgr.fatal(_("Renaming of space time datasets is not supported for PostgreSQL."))
+
         # SELECT all needed information from the database
         self.select(dbif)
 
@@ -1790,16 +1864,22 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         # Get the update statement, we update the table entry of the old
         # identifier
         statement = self.update(dbif, execute=False, ident=old_ident)
+
         # We need to rename the raster register table
-        statement += "ALTER TABLE %s RENAME TO \"%s\";\n" % \
-                     (old_map_register_table, new_map_register_table)
+        statement += "ALTER TABLE %s RENAME TO \"%s\";\n" % (old_map_register_table, 
+                                                             new_map_register_table)
+
+        # We need to take care of the stds index in the sqlite3 database
+        if dbif.dbmi.__name__ == "sqlite3":
+            statement += "DROP INDEX %s_index;\n" % (old_map_register_table)
+            statement += "CREATE INDEX %s_index ON %s (id);"%(new_map_register_table, 
+                                                              new_map_register_table)
 
         # We need to rename the space time dataset in the maps register table
         if maps:
             for map in maps:
-                map.select()
-                statement += "UPDATE %s SET id = \"%s\" WHERE id = \"%s\";\n"%\
-                             (map.get_stds_register(), ident, old_ident)
+                map.remove_dataset_from_register(stds_id=old_ident, dbif=dbif)
+                map.add_dataset_to_register(stds_id=ident, dbif=dbif)
 
         # Execute the accumulated statements
         dbif.execute_transaction(statement)
@@ -1829,7 +1909,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                     (self.get_new_map_instance(ident=None).get_type(),
                      self.get_id()))
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to delete dataset <%(ds)s> of type %(type)s from the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -1871,6 +1951,42 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
         return statement
 
+    def is_map_registered(self, map_id, dbif=None):
+        """!Check if a map is registered in the space time dataset
+        
+           @param map_id The map id
+           @param dbif The database interface to be used
+           @return True if success, False otherwise
+        """
+        stds_register_table = self.get_map_register()
+        
+        dbif, connected = init_dbif(dbif)
+        
+        is_registered = False
+        
+        # Check if map is already registered
+        if stds_register_table is not None:
+            if dbif.dbmi.paramstyle == "qmark":
+                sql = "SELECT id FROM " + \
+                    stds_register_table + " WHERE id = (?)"
+            else:
+                sql = "SELECT id FROM " + \
+                    stds_register_table + " WHERE id = (%s)"
+            try:
+                dbif.cursor.execute(sql, (map_id,))
+                row = dbif.cursor.fetchone()
+            except:
+                self.msgr.warning(_("Error in register table request"))
+                raise
+            
+            if row is not None and row[0] == map_id:
+                is_registered = True
+                
+        if connected == True:
+            dbif.close()
+
+        return is_registered
+        
     def register_map(self, map, dbif=None):
         """!Register a map in the space time dataset.
 
@@ -1880,14 +1996,14 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             In case the map is already registered this function
             will break with a warning and return False.
 
-            This method raises a ScriptError in case of a fatal error
+            This method raises a FatalError exception in case of a fatal error
 
            @param map The AbstractMapDataset object that should be registered
            @param dbif The database interface to be used
            @return True if success, False otherwise
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to register map in dataset <%(ds)s> of type %(type)s."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -1896,19 +2012,19 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
         if map.is_in_db(dbif) == False:
             dbif.close()
-            self.msgr.fatal(_("Only maps with absolute or relative valid time can "
-                         "be registered"))
+            self.msgr.fatal(_("Only a map that was inserted in the temporal database"
+                              " can be registered in a space time dataset"))
 
         if map.get_layer():
-            self.msgr.verbose(_("Register %s map <%s> with layer %s in space "
-                           "time %s dataset <%s>") % (map.get_type(),
-                                                      map.get_map_id(),
-                                                      map.get_layer(),
-                                                      map.get_type(),
-                                                      self.get_id()))
+            self.msgr.debug(1, "Register %s map <%s> with layer %s in space "
+                           "time %s dataset <%s>" % (map.get_type(),
+                                                     map.get_map_id(),
+                                                     map.get_layer(),
+                                                     map.get_type(),
+                                                     self.get_id()))
         else:
-            self.msgr.verbose(_("Register %s map <%s> in space time %s "
-                           "dataset <%s>") % (map.get_type(), map.get_map_id(),
+            self.msgr.debug(1, "Register %s map <%s> in space time %s "
+                           "dataset <%s>" % (map.get_type(), map.get_map_id(),
                                               map.get_type(), self.get_id()))
 
         # First select all data from the database
@@ -1921,17 +2037,12 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             else:
                 self.msgr.fatal(_("Map <%s> has invalid time") % (map.get_map_id()))
 
+        # Get basic info
         map_id = map.base.get_id()
-        map_name = map.base.get_name()
         map_mapset = map.base.get_mapset()
-        map_register_table = map.get_stds_register()
         map_rel_time_unit = map.get_relative_time_unit()
         map_ttype = map.get_temporal_type()
 
-        #print "Map register table", map_register_table
-
-        # Get basic info
-        stds_name = self.base.get_name()
         stds_mapset = self.base.get_mapset()
         stds_register_table = self.get_map_register()
         stds_ttype = self.get_temporal_type()
@@ -1985,128 +2096,20 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             self.msgr.fatal(_("Only maps from the same mapset can be registered"))
 
         # Check if map is already registered
-        if stds_register_table is not None:
-            if dbif.dbmi.paramstyle == "qmark":
-                sql = "SELECT id FROM " + \
-                    stds_register_table + " WHERE id = (?)"
+        if self.is_map_registered(map_id, dbif=dbif):
+            if map.get_layer() is not None:
+                self.msgr.warning(_("Map <%(map)s> with layer %(l)s is already "
+                                "registered.") % {'map': map.get_map_id(),
+                                                    'l': map.get_layer()})
             else:
-                sql = "SELECT id FROM " + \
-                    stds_register_table + " WHERE id = (%s)"
-            try:
-                dbif.cursor.execute(sql, (map_id,))
-                row = dbif.cursor.fetchone()
-            except:
-                self.msgr.warning(_("Error in strds_register_table request"))
-                raise
-
-            if row is not None and row[0] == map_id:
-                if connected == True:
-                    dbif.close()
-
-                if map.get_layer() is not None:
-                    self.msgr.warning(_("Map <%(map)s> with layer %(l)s is already "
-                                   "registered.") % {'map': map.get_map_id(),
-                                                     'l': map.get_layer()})
-                else:
-                    self.msgr.warning(_("Map <%s> is already registered.") % (
-                                 map.get_map_id()))
-                return False
-
-        # Create tables
-        sql_path = get_sql_template_path()
-
-        # We need to create the map raster register table precedes we can
-        # register the map
-        if map_register_table is None:
-            # Create a unique id
-            uuid_rand = "map_" + str(uuid.uuid4()).replace("-", "")
-
-            map_register_table = uuid_rand + "_" + \
-                self.get_type() + "_register"
-
-            # Read the SQL template
-            sql = open(os.path.join(sql_path,
-                                    "map_stds_register_table_template.sql"),
-                                    'r').read()
-            # Create the raster, raster3d and vector tables
-            sql = sql.replace("GRASS_MAP", map.get_type())
-            sql = sql.replace("MAP_NAME", map_name + "_" + map_mapset)
-            sql = sql.replace("TABLE_NAME", uuid_rand)
-            sql = sql.replace("MAP_ID", map_id)
-            sql = sql.replace("STDS", self.get_type())
-
-            statement += sql
-
-            # Set the stds register table name and put it into the DB
-            map.set_stds_register(map_register_table)
-            statement += map.metadata.get_update_statement_mogrified(dbif)
-
-            if map.get_layer():
-                self.msgr.debug(1, _("Created register table <%s> for "
-                               "%s map <%s> with layer %s") %
-                                (map_register_table, map.get_type(),
-                                 map.get_map_id(), map.get_layer()))
-            else:
-                self.msgr.debug(1, _("Created register table <%s> for %s map <%s>") %
-                                (map_register_table, map.get_type(),
-                                 map.get_map_id()))
-
-        # We need to create the table and register it
-        if stds_register_table is None:
-            # Create table name
-            stds_register_table = self.create_map_register_name()
-            # Assure that the table and index do not exist
-            dbif.execute_transaction("DROP INDEX IF EXISTS %s; DROP TABLE IF EXISTS  %s;"%(stds_register_table + "_index", stds_register_table))
-
-            # Read the SQL template
-            sql = open(os.path.join(sql_path,
-                                    "stds_map_register_table_template.sql"),
-                                    'r').read()
-            # Create the raster, raster3d and vector tables
-            sql = sql.replace("GRASS_MAP", map.get_type())
-            sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset)
-            sql = sql.replace("SPACETIME_ID", self.base.get_id())
-            sql = sql.replace("STDS", self.get_type())
-            statement += sql
-
-            # Set the map register table name and put it into the DB
-            self.set_map_register(stds_register_table)
-            statement += self.metadata.get_update_statement_mogrified(dbif)
-
-            self.msgr.debug(1, _("Created register table <%s> for space "
-                           "time %s  dataset <%s>") %
-                          (stds_register_table, map.get_type(), self.get_id()))
-
-        # We need to execute the statement at this time
-        if statement != "":
-            dbif.execute_transaction(statement)
-
-        statement = ""
-
-        # Register the stds in the map stds register table
-        # Check if the entry is already there
-        if dbif.dbmi.paramstyle == "qmark":
-            sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
-        else:
-            sql = "SELECT id FROM " + map_register_table + " WHERE id = %s"
-        try:
-            dbif.cursor.execute(sql, (self.base.get_id(),))
-            row = dbif.cursor.fetchone()
-        except:
-            row = None
-
-        # In case of no entry make a new one
-        if row is None:
-            if dbif.dbmi.paramstyle == "qmark":
-                sql = "INSERT INTO " + map_register_table + \
-                    " (id) " + "VALUES (?);\n"
-            else:
-                sql = "INSERT INTO " + map_register_table + \
-                    " (id) " + "VALUES (%s);\n"
-
-            statement += dbif.mogrify_sql_statement(
-                (sql, (self.base.get_id(),)))
+                self.msgr.warning(_("Map <%s> is already registered.") % (
+                                map.get_map_id()))
+            return False
 
+        # Register the stds in the map stds register table column
+        statement += map.add_dataset_to_register(stds_id=self.base.get_id(), 
+                                                 dbif=dbif, execute=False)
+        
         # Now put the raster name in the stds map register table
         if dbif.dbmi.paramstyle == "qmark":
             sql = "INSERT INTO " + stds_register_table + \
@@ -2114,6 +2117,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         else:
             sql = "INSERT INTO " + stds_register_table + \
                 " (id) " + "VALUES (%s);\n"
+                
 
         statement += dbif.mogrify_sql_statement((sql, (map_id,)))
 
@@ -2145,7 +2149,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                    string, None in case of a failure
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to unregister map from dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -2154,53 +2158,29 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
         dbif, connected = init_dbif(dbif)
 
-        # First select needed data from the database
-        map.metadata.select(dbif)
-
-        map_id = map.get_id()
-        map_register_table = map.get_stds_register()
-        stds_register_table = self.get_map_register()
-
         # Check if the map is registered in the space time raster dataset
-        if map_register_table is not None:
-            if dbif.dbmi.paramstyle == "qmark":
-                sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
-            else:
-                sql = "SELECT id FROM " + map_register_table + " WHERE id = %s"
-            try:
-                dbif.cursor.execute(sql, (self.base.get_id(),))
-                row = dbif.cursor.fetchone()
-            except:
-                row = None
-
-            # Break if the map is not registered
-            if row is None:
-                if map.get_layer() is not None:
-                    self.msgr.warning(_("Map <%(map)s> with layer %(l)s is not "
-                                   "registered in space time dataset "
-                                   "<%(base)s>") % {'map': map.get_map_id(),
-                                                    'l': map.get_layer(),
-                                                    'base': self.base.get_id()})
-                else:
-                    self.msgr.warning(_("Map <%(map)s> is not registered in space "
-                                   "time dataset <%(base)s>") % {
-                                   'map': map.get_map_id(),
-                                   'base': self.base.get_id()})
-                if connected == True:
-                    dbif.close()
-                return ""
-
-        # Remove the space time raster dataset from the raster dataset register
-        if map_register_table is not None:
-            if dbif.dbmi.paramstyle == "qmark":
-                sql = "DELETE FROM " + map_register_table + " WHERE id = ?;\n"
+        if self.is_map_registered(map.get_id(), dbif) is False:
+            if map.get_layer() is not None:
+                self.msgr.warning(_("Map <%(map)s> with layer %(l)s is not "
+                                "registered in space time dataset "
+                                "<%(base)s>") % {'map': map.get_map_id(),
+                                'l': map.get_layer(), 'base': self.base.get_id()})
             else:
-                sql = "DELETE FROM " + map_register_table + " WHERE id = %s;\n"
-
-            statement += dbif.mogrify_sql_statement(
-                (sql, (self.base.get_id(),)))
+                self.msgr.warning(_("Map <%(map)s> is not registered in space "
+                                "time dataset <%(base)s>") % {'map': map.get_map_id(),
+                                'base': self.base.get_id()})
+            if connected == True:
+                dbif.close()
+            return ""
 
-        # Remove the raster map from the space time raster dataset register
+        # Remove the space time dataset from the dataset register
+        # We need to execute the statement here, otherwise the space time dataset will not be
+        # removed correctly
+        map.remove_dataset_from_register(self.base.get_id(), 
+                                                      dbif=dbif, execute=True)
+        
+        # Remove the map from the space time dataset register
+        stds_register_table = self.get_map_register()
         if stds_register_table is not None:
             if dbif.dbmi.paramstyle == "qmark":
                 sql = "DELETE FROM " + stds_register_table + " WHERE id = ?;\n"
@@ -2208,10 +2188,11 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                 sql = "DELETE FROM " + \
                     stds_register_table + " WHERE id = %s;\n"
 
-            statement += dbif.mogrify_sql_statement((sql, (map_id,)))
+            statement += dbif.mogrify_sql_statement((sql, ( map.get_id(),)))
 
         if execute:
             dbif.execute_transaction(statement)
+            statement = ""
 
         if connected:
             dbif.close()
@@ -2219,13 +2200,10 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         # decrease the counter
         self.map_counter -= 1
 
-        if execute:
-            return ""
-
         return statement
 
     def update_from_registered_maps(self, dbif=None):
-        """!This methods updates the spatial and temporal extent as well as
+        """!This methods updates the modification time, the spatial and temporal extent as well as
            type specific metadata. It should always been called after maps
            are registered or unregistered/deleted from the space time dataset.
 
@@ -2236,14 +2214,10 @@ class AbstractSpaceTimeDataset(AbstractDataset):
            will be used. If the end time is earlier than the maximum start
            time, it will be replaced by the maximum start time.
 
-           An other solution to automate this is to use the deactivated trigger
-           in the SQL files. But this will result in a huge performance issue
-           in case many maps are registered (>1000).
-
            @param dbif The database interface to be used
         """
 
-        if self.get_mapset() != get_current_mapset():
+        if get_enable_mapset_check() is True and self.get_mapset() != get_current_mapset():
             self.msgr.fatal(_("Unable to update dataset <%(ds)s> of type %(type)s in the temporal database."
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -2301,7 +2275,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         self.select()
 
         if self.is_time_absolute():
-            start_time, end_time, tz = self.get_absolute_time()
+            start_time, end_time = self.get_absolute_time()
         else:
             start_time, end_time, unit = self.get_relative_time()
 
@@ -2418,6 +2392,10 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             self.temporal_extent.set_map_time(None)
             self.temporal_extent.set_granularity(None)
         self.temporal_extent.update_all(dbif)
+        
+        # Set the modification time
+        self.base.set_mtime(datetime.now())
+        self.base.update(dbif)
 
         if connected:
             dbif.close()

+ 123 - 43
lib/python/temporal/base.py

@@ -523,7 +523,6 @@ class SQLDatabaseInterface(DictSQLSerializer):
 
 ###############################################################################
 
-
 class DatasetBase(SQLDatabaseInterface):
     """!This is the base class for all maps and spacetime datasets storing
         basic identification information
@@ -552,15 +551,15 @@ class DatasetBase(SQLDatabaseInterface):
          | Name: ...................... soil
          | Mapset: .................... PERMANENT
          | Creator: ................... soeren
-         | Creation time: ............. 2001-01-01 00:00:00
          | Temporal type: ............. absolute
+         | Creation time: ............. 2001-01-01 00:00:00
         >>> t.print_shell_info()
         id=soil@PERMANENT
         name=soil
         mapset=PERMANENT
         creator=soeren
-        creation_time=2001-01-01 00:00:00
         temporal_type=absolute
+        creation_time=2001-01-01 00:00:00
 
         \endcode
     """
@@ -597,9 +596,6 @@ class DatasetBase(SQLDatabaseInterface):
         self.set_creator(creator)
         self.set_ctime(ctime)
         self.set_ttype(ttype)
-        # Commented out for performance reasons
-        #self.set_mtime(mtime)
-        #self.set_revision(revision)
 
     def set_id(self, ident):
         """!Convenient method to set the unique identifier (primary key)
@@ -676,17 +672,6 @@ class DatasetBase(SQLDatabaseInterface):
         else:
             self.D["temporal_type"] = ttype
 
-#    def set_mtime(self, mtime=None):
-#       """!Set the modification time of the map, if nothing set the current time is used"""
-#       if mtime == None:
-#            self.D["modification_time"] = datetime.now()
-#       else:
-#            self.D["modification_time"] = mtime
-
-#    def set_revision(self, revision=1):
-#       """!Set the revision of the map: if nothing set revision 1 will assumed"""
-#       self.D["revision"] = revision
-
     def get_id(self):
         """!Convenient method to get the unique identifier (primary key)
 
@@ -766,22 +751,6 @@ class DatasetBase(SQLDatabaseInterface):
         else:
             return None
 
-#    def get_mtime(self):
-#       """!Get the modification time of the map, datatype is datetime
-#          @return None if not found"""
-#       if self.D.has_key("modification_time"):
-#           return self.D["modification_time"]
-#       else:
-#           return None
-
-#    def get_revision(self):
-#       """!Get the revision of the map
-#          @return None if not found"""
-#       if self.D.has_key("revision"):
-#           return self.D["revision"]
-#       else:
-#           return None
-
     # Properties of this class
     id = property(fget=get_id, fset=set_id)
     map_id = property(fget=get_map_id, fset=None)
@@ -801,10 +770,8 @@ class DatasetBase(SQLDatabaseInterface):
         if self.get_layer():
             print " | Layer:...................... " + str(self.get_layer())
         print " | Creator: ................... " + str(self.get_creator())
-        print " | Creation time: ............. " + str(self.get_ctime())
         print " | Temporal type: ............. " + str(self.get_ttype())
-#        print " | Modification time: ......... " + str(self.get_mtime())
-#        print " | Revision in database: ...... " + str(self.get_revision())
+        print " | Creation time: ............. " + str(self.get_ctime())
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
@@ -814,10 +781,8 @@ class DatasetBase(SQLDatabaseInterface):
         if self.get_layer():
             print "layer=" + str(self.get_layer())
         print "creator=" + str(self.get_creator())
-        print "creation_time=" + str(self.get_ctime())
         print "temporal_type=" + str(self.get_ttype())
-#        print "modification_time=" + str(self.get_mtime())
-#        print "revision=" + str(self.get_revision())
+        print "creation_time=" + str(self.get_ctime())
 
 ###############################################################################
 
@@ -872,7 +837,7 @@ class STDSBase(DatasetBase):
     \code
 
     >>> 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", mtime=datetime(2001,1,1))
     >>> t.semantic_type
     'average'
     >>> t.print_info()
@@ -881,31 +846,41 @@ class STDSBase(DatasetBase):
      | Name: ...................... soil
      | Mapset: .................... PERMANENT
      | Creator: ................... soeren
-     | Creation time: ............. 2001-01-01 00:00:00
      | Temporal type: ............. absolute
+     | Creation time: ............. 2001-01-01 00:00:00
+     | Modification time:.......... 2001-01-01 00:00:00
      | Semantic type:.............. average
     >>> t.print_shell_info()
     id=soil@PERMANENT
     name=soil
     mapset=PERMANENT
     creator=soeren
-    creation_time=2001-01-01 00:00:00
     temporal_type=absolute
+    creation_time=2001-01-01 00:00:00
+    modification_time=2001-01-01 00:00:00
     semantic_type=average
 
     \endcode
     """
     def __init__(self, table=None, ident=None, name=None, mapset=None,
                  semantic_type=None, creator=None, ctime=None,
-                 ttype=None):
+                 ttype=None, mtime=None):
         DatasetBase.__init__(self, table, ident, name, mapset, creator,
                               ctime, ttype)
 
         self.set_semantic_type(semantic_type)
+        self.set_mtime(mtime)
 
     def set_semantic_type(self, semantic_type):
         """!Set the semantic type of the space time dataset"""
         self.D["semantic_type"] = semantic_type
+        
+    def set_mtime(self, mtime=None):
+       """!Set the modification time of the space time dataset, if nothing set the current time is used"""
+       if mtime == None:
+            self.D["modification_time"] = datetime.now()
+       else:
+            self.D["modification_time"] = mtime
 
     def get_semantic_type(self):
         """!Get the semantic type of the space time dataset
@@ -914,6 +889,14 @@ class STDSBase(DatasetBase):
             return self.D["semantic_type"]
         else:
             return None
+            
+    def get_mtime(self):
+       """!Get the modification time of the space time dataset, datatype is datetime
+          @return None if not found"""
+       if self.D.has_key("modification_time"):
+           return self.D["modification_time"]
+       else:
+           return None
 
     semantic_type = property(fget=get_semantic_type, fset=set_semantic_type)
 
@@ -921,12 +904,14 @@ class STDSBase(DatasetBase):
         """!Print information about this class in human readable style"""
         DatasetBase.print_info(self)
         #      0123456789012345678901234567890
+        print " | Modification time:.......... " + str(self.get_mtime())
         print " | Semantic type:.............. " + str(
             self.get_semantic_type())
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         DatasetBase.print_shell_info(self)
+        print "modification_time=" + str(self.get_mtime())
         print "semantic_type=" + str(self.get_semantic_type())
 
 ###############################################################################
@@ -963,6 +948,101 @@ class STVDSBase(STDSBase):
 
 ###############################################################################
 
+class AbstractSTDSRegister(SQLDatabaseInterface):
+    """!This is the base class for all maps to store the space time datasets
+       as comma separated string in which they are registered
+
+        Usage:
+
+        \code
+
+        >>> init()
+        >>> t = AbstractSTDSRegister("raster", "soil@PERMANENT", "A@P,B@P,C@P")
+        >>> t.id
+        'soil@PERMANENT'
+        >>> t.registered_stds
+        'A@P,B@P,C@P'
+
+        \endcode
+    """
+
+    def __init__(self, table=None, ident=None, registered_stds=None):
+        """!Constructor
+
+            @param table The name of the temporal database table
+                          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
+                          "name@mapset" or "name:layer@mapset"
+                          used as as primary key in the temporal database
+            @param stds A comma separted list of space time dataset ids
+        """
+
+        SQLDatabaseInterface.__init__(self, table, ident)
+
+        self.set_id(ident)
+        self.set_registered_stds(registered_stds)
+
+    def set_id(self, ident):
+        """!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
+                         "name@mapset" or "name:layer@mapset"
+        """
+        self.ident = ident
+        self.D["id"] = ident
+
+    def set_registered_stds(self, registered_stds):
+        """!Get the comma separated list of space time datasets ids
+           in which this map is registered
+           @param registered_stds A comma separated list of space time dataset ids
+                                  in which this map is registered
+        """
+        self.D["registered_stds"] = registered_stds
+
+    def get_id(self):
+        """!Convenient method to get the unique identifier (primary key)
+
+           @return None if not found
+        """
+        if "id" in self.D:
+            return self.D["id"]
+        else:
+            return None
+
+    def get_registered_stds(self):
+        """!Get the comma separated list of space time datasets ids
+           in which this map is registered
+           @return None if not found"""
+        if "registered_stds" in self.D:
+            return self.D["registered_stds"]
+        else:
+            return None
+
+    # Properties of this class
+    id = property(fget=get_id, fset=set_id)
+    registered_stds = property(fget=get_registered_stds, fset=set_registered_stds)
+
+###############################################################################
+
+class RasterSTDSRegister(AbstractSTDSRegister):
+    """!Time stamped raster map base information class"""
+    def __init__(self, ident=None, registered_stds=None):
+        AbstractSTDSRegister.__init__(self, "raster_stds_register", ident, registered_stds)
+
+class Raster3DSTDSRegister(AbstractSTDSRegister):
+    """!Time stamped 3D raster map base information class"""
+    def __init__(self, ident=None, registered_stds=None):
+        AbstractSTDSRegister.__init__(self, "raster3d_stds_register", ident, registered_stds)
+
+class VectorSTDSRegister(AbstractSTDSRegister):
+    """!Time stamped vector map base information class"""
+    def __init__(self, ident=None, registered_stds=None):
+        AbstractSTDSRegister.__init__(self, "vector_stds_register", ident, registered_stds)
+
+###############################################################################
+
 if __name__ == "__main__":
     import doctest
     doctest.testmod()

+ 163 - 51
lib/python/temporal/core.py

@@ -34,7 +34,13 @@ for details.
 
 @author Soeren Gebbert
 """
+import sys, traceback
 import os
+import locale
+# i18N
+import gettext
+gettext.install('grasslibs', os.path.join(os.getenv("GISBASE"), 'locale'))
+
 import grass.script.core as core
 from datetime import datetime
 from c_libraries_interface import *
@@ -51,8 +57,25 @@ try:
 except:
     pass
 
-# Uncomment this to raise and exception in case of a fatal error
-# core.set_raise_on_error(True)
+###############################################################################
+
+# Profiling function provided by the temporal framework
+def profile_function(func):
+    do_profiling = os.getenv("GRASS_TGIS_PROFILE")
+    
+    if do_profiling is not None:
+        import cProfile, pstats, StringIO
+        pr = cProfile.Profile()
+        pr.enable()
+        func()
+        pr.disable()
+        s = StringIO.StringIO()
+        sortby = 'cumulative'
+        ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
+        ps.print_stats()
+        print s.getvalue()
+    else:
+        func()
 
 # Global variable that defines the backend
 # of the temporal GIS
@@ -62,12 +85,12 @@ tgis_backend = None
 # The version of the temporal framework
 # this value must be an integer larger than 0
 # Increase this value in case of backward incompatible changes in the TGIS API
-tgis_version=1
+tgis_version=2
 # The version of the temporal database since framework and database version can differ
 # this value must be an integer larger than 0
 # Increase this value in case of backward incompatible changes
 # temporal database SQL layout
-tgis_db_version=1
+tgis_db_version=2
 
 # We need to access the current mapset quite often in the framework, so we make
 # global variable that will be initiated when init() is called
@@ -101,6 +124,54 @@ def _set_current_mapset(mapset=None):
 
 ###############################################################################
 
+# If this global variable is set True, then maps can only be registered in space time datasets
+# with the same mapset. In addition, only maps in the current mapset can be inserted, updated or deleted from
+# the temporal database. 
+# Overwrite this global variable by: g.gisenv set="TGIS_DISABLE_MAPSET_CHECK=True"
+# ATTENTION: Be aware to face corrupted temporal database in case this global variable is set to False.
+#            This feature is highly experimental and violates the grass permission guidance.
+enable_mapset_check = True
+# If this global variable is set True, the timestamps of maps will be written as textfiles
+# for each map that will be inserted or updated in the temporal database using the C-library 
+# timestamp interface.
+# Overwrite this global variable by: g.gisenv set="TGIS_DISABLE_TIMESTAMP_WRITE=True"
+# ATTENTION: Be aware to face corrupted temporal database in case this global variable is set to False.
+#            This feature is highly experimental and violates the grass permission guidance.
+enable_timestamp_write = True
+
+def get_enable_mapset_check():
+    """!Return True if the mapsets should be checked while insert, updatem delete requests
+       and space time dataset registration.
+       
+       If this global variable is set True, then maps can only be registered in space time datasets
+       with the same mapset. In addition, only maps in the current mapset can be inserted, updated or deleted from
+       the temporal database. 
+       Overwrite this global variable by: g.gisenv set="TGIS_DISABLE_MAPSET_CHECK=True"
+       
+       ATTENTION: Be aware to face corrupted temporal database in case this global variable is set to False.
+                  This feature is highly experimental and violates the grass permission guidance.
+    """
+    global enable_mapset_check
+    return enable_mapset_check
+    
+def get_enable_timestamp_write():
+    """!Return True if the map timestamps should be written to the spatial database metadata as well.
+    
+       If this global variable is set True, the timestamps of maps will be written as textfiles
+       for each map that will be inserted or updated in the temporal database using the C-library 
+       timestamp interface.
+       Overwrite this global variable by: g.gisenv set="TGIS_DISABLE_TIMESTAMP_WRITE=True"
+       
+       ATTENTION: Be aware that C-libraries can not access timestamp informations if they are not
+                  written as spatial database metadata, hence modules that make use of timestamps 
+                  using the C-library interface will not work with maps that were created without 
+                  writing the timestamps.
+    """
+    global enable_timestamp_write
+    return enable_timestamp_write
+
+###############################################################################
+
 # The global variable that stores the PyGRASS Messenger object that
 # provides a fast and exit safe interface to the C-library message functions
 message_interface=None
@@ -159,6 +230,15 @@ def get_tgis_version():
 
 ###############################################################################
 
+def get_tgis_db_version():
+    """!Get the verion number of the temporal framework
+       @return The version number of the temporal framework as string
+    """
+    global tgis_db_version
+    return tgis_db_version
+
+###############################################################################
+
 def get_tgis_metadata(dbif=None):
     """!Return the tgis metadata table as a list of rows (dicts)
                or None if not present
@@ -190,6 +270,9 @@ def get_temporal_dbmi_init_string(kv=None, grassenv=None):
        @param kv dictionary generated by grass.script.parse_command("t.connect", flags="pg")
        @param grassenv Grass environemntal variables created by grass.script.gisenv()
     """
+
+    global temporal_dbmi_init_string
+
     if kv == None:
         kv = core.parse_command("t.connect", flags="pg")
     if grassenv == None:
@@ -207,6 +290,7 @@ def get_temporal_dbmi_init_string(kv=None, grassenv=None):
             string = string.replace(
                 "$LOCATION_NAME", grassenv["LOCATION_NAME"])
             string = string.replace("$MAPSET", grassenv["MAPSET"])
+            temporal_dbmi_init_string = string
             return string
         else:
             msgr.fatal(_("Unable to initialize the temporal GIS DBMI "
@@ -215,6 +299,7 @@ def get_temporal_dbmi_init_string(kv=None, grassenv=None):
     elif tgis_backend == "pg":
         if "database" in kv:
             string = kv["database"]
+            temporal_dbmi_init_string = string
             return string
     else:
         msgr.fatal(_("Unable to initialize the temporal GIS DBMI "
@@ -230,34 +315,64 @@ def get_sql_template_path():
 
 ###############################################################################
 
-def init(raise_on_error=False):
-    """!This function set the correct database backend from the environmental variables
+def init():
+    """!This function set the correct database backend from GRASS environmental variables
        and creates the grass location database structure for raster,
        vector and raster3d maps as well as for the space-time datasets strds,
-       str3ds and stvds in case it not exists.
+       str3ds and stvds in case it does not exists.
+       
+       Several global variables are initiated and the messenger and C-library interface
+       subprocesses are spawned.
+       
+       The following g.gisenv variables are checked:
+        - TGIS_RAISE_ON_ERROR
+        - TGIS_DISABLE_MAPSET_CHECK
+        - TGIS_DISABLE_TIMESTAMP_WRITE
+        
+       The following environmental variables are checked:
+        - GRASS_TGIS_PROFILE
 
         ATTENTION: This functions must be called before any spatio-temporal processing
                    can be started
-
-       @param raise_on_error If True raise a FatalError exception in case of a fatal error,
-                             call sys.exit(1) otherwise
-
     """
     # We need to set the correct database backend from the environment variables
     global tgis_backend
-
+    global enable_mapset_check
+    global enable_timestamp_write
+    
     core.run_command("t.connect", flags="c")
     kv = core.parse_command("t.connect", flags="pg")
     grassenv = core.gisenv()
+    raise_on_error = False
 
+    # Check the g.gisenv variable TGIS_RAISE_ON_ERROR
+    if grassenv.has_key("TGIS_RAISE_ON_ERROR"):
+        if grassenv["TGIS_RAISE_ON_ERROR"] == "True" or grassenv["TGIS_RAISE_ON_ERROR"] == "1":
+            raise_on_error = True
+            
     # Set the global variable current_mapset for fast mapset access
     _set_current_mapset(grassenv["MAPSET"])
     # Start the GRASS message interface server
     _init_tgis_message_interface(raise_on_error)
     # Start the C-library interface server
     _init_tgis_c_library_interface()
-
+    
     msgr = get_tgis_message_interface()
+    msgr.debug(1, "Inititate the temporal database")
+    
+    if raise_on_error is True:
+        msgr.warning("TGIS_RAISE_ON_ERROR is True")
+
+    # Set the mapset check and the timestamp write
+    if grassenv.has_key("TGIS_DISABLE_MAPSET_CHECK"):
+        if grassenv["TGIS_DISABLE_MAPSET_CHECK"] == "True" or grassenv["TGIS_DISABLE_MAPSET_CHECK"] == "1":
+            enable_mapset_check = False
+            msgr.warning("TGIS_DISABLE_MAPSET_CHECK is True")
+
+    if grassenv.has_key("TGIS_DISABLE_TIMESTAMP_WRITE"):
+        if grassenv["TGIS_DISABLE_TIMESTAMP_WRITE"] == "True" or grassenv["TGIS_DISABLE_TIMESTAMP_WRITE"] == "1":
+            enable_timestamp_write = False
+            msgr.warning("TGIS_DISABLE_TIMESTAMP_WRITE is True")
 
     if "driver" in kv:
         if kv["driver"] == "sqlite":
@@ -309,35 +424,24 @@ def init(raise_on_error=False):
         if dbif.cursor.fetchone()[0]:
             db_exists = True
 
-    if db_exists:
-        # Check if we have to add the command column
-        add_command_col = True
-        rows = get_tgis_metadata(dbif)
-        if rows:
-            for row in rows:
-                if row["key"] == "tgis_db_version":
-                    version = int(row["value"])
-                    if version >= 1:
-                        add_command_col = False
-
-        if add_command_col:
-            # Try to add the command column to the space time dataset metadata tables
-            # this is due backward compatibility with old databases
-            try:
-                dbif.cursor.execute('ALTER TABLE strds_metadata ADD COLUMN command VARCHAR;')
-            except:
-                pass
-            try:
-                dbif.cursor.execute('ALTER TABLE str3ds_metadata ADD COLUMN command VARCHAR;')
-            except:
-                pass
-            try:
-                dbif.cursor.execute('ALTER TABLE stvds_metadata ADD COLUMN command VARCHAR;')
-            except:
-                pass
-
     if db_exists == True:
+        
+        # Check the version of the temporal database
+        # This version works only with database of version 2
+        dbif.close()
+        dbif.connect()
+        metadata = get_tgis_metadata(dbif)
         dbif.close()
+        if metadata is None:
+            msgr.fatal(_("Unable to receiving temporal database metadata. Your temporal database is not supported."))
+        
+        for entry in metadata:
+            if "tgis_version" in entry and entry[1] != str(get_tgis_version()):
+                msgr.fatal(_("Unsupported temporal database. Version mismatch.\n"
+                "Supported temporal API version is: %(api)i")%({"api":get_tgis_version()}))
+            if "tgis_db_version" in entry and entry[1] != str(get_tgis_db_version()):
+                msgr.fatal(_("Unsupported temporal database. Version mismatch.\n"
+                "Supported temporal database version is: %(tdb)i")%( {"tdb":get_tgis_db_version()}))
         return
 
     create_temporal_database(dbif, database)
@@ -415,10 +519,19 @@ def create_temporal_database(dbif, database):
         tgis_dir = os.path.dirname(database)
         if not os.path.exists(tgis_dir):
             os.makedirs(tgis_dir)
-        # Sqlite needs some trigger to emulate the foreign keys
-        sqlite3_delete_trigger_sql = open(os.path.join(template_path,
-                                                       "sqlite3_delete_trigger.sql"),
-                                                       'r').read()
+        # Set up the trigger that takes care of
+        # the correct deletion of entries across the different tables
+        delete_trigger_sql = open(os.path.join(template_path,
+                                               "sqlite3_delete_trigger.sql"),
+                                               'r').read()
+        indexes_sql = open(os.path.join(template_path, "sqlite3_indexes.sql"), 'r').read()
+    else:
+        # Set up the trigger that takes care of
+        # the correct deletion of entries across the different tables
+        delete_trigger_sql = open(os.path.join(template_path,
+                                            "postgresql_delete_trigger.sql"),
+                                            'r').read()
+        indexes_sql = open(os.path.join(template_path, "postgresql_indexes.sql"), 'r').read()
 
     # Connect now to the database
     if not dbif.connected:
@@ -446,8 +559,10 @@ def create_temporal_database(dbif, database):
     dbif.execute_transaction(str3ds_metadata_sql)
     dbif.execute_transaction(str3ds_views_sql)
 
-    if tgis_backend == "sqlite":
-        dbif.execute_transaction(sqlite3_delete_trigger_sql)
+    # The delete trigger
+    dbif.execute_transaction(delete_trigger_sql)
+    # The indexes
+    dbif.execute_transaction(indexes_sql)
 
     # Create the tgis metadata table to store the database
     # initial configuration
@@ -455,7 +570,6 @@ def create_temporal_database(dbif, database):
     metadata = {}
     metadata["tgis_version"] = tgis_version
     metadata["tgis_db_version"] = tgis_db_version
-    metadata["has_command_column"] = True
     metadata["creation_time"] = datetime.today()
     _create_tgis_metadata_table(metadata, dbif)
 
@@ -502,7 +616,7 @@ class SQLDatabaseInterfaceConnection():
         else:
             self.dbmi = psycopg2
 
-        msgr = get_tgis_message_interface()
+        self.msgr = get_tgis_message_interface()
 
     def rollback(self):
         """
@@ -521,7 +635,6 @@ class SQLDatabaseInterfaceConnection():
            Supported backends are sqlite3 and postgresql
         """
         self.database = get_temporal_dbmi_init_string()
-        #print "Connect to",  self.database
         if self.dbmi.__name__ == "sqlite3":
             self.connection = self.dbmi.connect(self.database,
                     detect_types = self.dbmi.PARSE_DECLTYPES | self.dbmi.PARSE_COLNAMES)
@@ -680,8 +793,7 @@ class SQLDatabaseInterfaceConnection():
         except:
             if connected:
                 self.close()
-            msgr.error(_("Unable to execute transaction:\n %(sql)s" % \
-                         {"sql":statement}))
+            self.msgr.error(_("Unable to execute transaction:\n %(sql)s" % {"sql":statement}))
             raise
 
         if connected:

+ 58 - 8
lib/python/temporal/datetime_math.py

@@ -15,6 +15,12 @@ from datetime import datetime, date, time, timedelta
 from core import *
 import copy
 
+try:
+    import dateutil.parser as parser
+    has_dateutil = True
+except:
+    has_dateutil = False
+
 DAY_IN_SECONDS = 86400
 SECOND_AS_DAY = 1.1574074074074073e-05
 
@@ -708,6 +714,24 @@ def check_datetime_string(time_string):
         @return datetime object or an error message string in case of an error
     """
 
+    global has_dateutil
+        
+    if has_dateutil:
+        # First check if there is only a single number, which specifies relative time.
+        # dateutil will interprete a single number as a valid time string, so we have
+        # to catch this case beforehand
+        try:
+            value = int(time_string)
+            return _("Time string seems to specify relative time")
+        except ValueError:
+            pass
+        
+        try:
+            time_object = parser.parse(time_string)
+        except Exception as inst:
+            time_object = str(inst)
+        return time_object
+        
     # BC is not supported
     if time_string.find("bc") > 0:
         return _("Dates Before Christ are not supported")
@@ -732,11 +756,13 @@ def check_datetime_string(time_string):
 def string_to_datetime(time_string):
     """!Convert a string into a datetime object
 
-        Supported ISO string formats are:
+        In case datutil is not installed the supported ISO string formats are:
         - YYYY-mm-dd
         - YYYY-mm-dd HH:MM:SS
-
-        Time zones are not supported
+        - Time zones are not supported
+        
+        If dateutil is installed, all string formats of the dateutil module
+        are supported, as well as time zones
 
         @param time_string The time string to convert
         @return datetime object or None in case the string 
@@ -754,23 +780,47 @@ def string_to_datetime(time_string):
 
     return time_object
 
-
-
 ###############################################################################
 
 
 def datetime_to_grass_datetime_string(dt):
-    """!Convert a python datetime object into a GRASS datetime string"""
-
+    """!Convert a python datetime object into a GRASS datetime string
+    
+    @code
+    
+    >>> import grass.temporal as tgis
+    >>> import dateutil.parser as parser
+    >>> dt = parser.parse("2011-01-01 10:00:00 +01:30")
+    >>> tgis.datetime_to_grass_datetime_string(dt)
+    '01 jan 2011 10:00:00 +0090'
+    >>> dt = parser.parse("2011-01-01 10:00:00 +02:30")
+    >>> tgis.datetime_to_grass_datetime_string(dt)
+    '01 jan 2011 10:00:00 +0150'
+    >>> dt = parser.parse("2011-01-01 10:00:00 +12:00")
+    >>> tgis.datetime_to_grass_datetime_string(dt)
+    '01 jan 2011 10:00:00 +0720'
+    >>> dt = parser.parse("2011-01-01 10:00:00 -01:30")
+    >>> tgis.datetime_to_grass_datetime_string(dt)
+    '01 jan 2011 10:00:00 -0090'
+    
+    @endcode
+    """
     # GRASS datetime month names
     month_names = ["", "jan", "feb", "mar", "apr", "may", "jun",
                    "jul", "aug", "sep", "oct", "nov", "dec"]
 
     # Check for time zone info in the datetime object
     if dt.tzinfo is not None:
+        
+        tz = dt.tzinfo.utcoffset(0)
+        if tz.seconds > 86400 / 2:
+            tz = (tz.seconds - 86400) / 60
+        else:
+            tz = tz.seconds/60
+            
         string = "%.2i %s %.2i %.2i:%.2i:%.2i %+.4i" % (dt.day,
                  month_names[dt.month], dt.year,
-                 dt.hour, dt.minute, dt.second, dt.tzinfo._offset.seconds / 60)
+                 dt.hour, dt.minute, dt.second, tz)
     else:
         string = "%.2i %s %.4i %.2i:%.2i:%.2i" % (dt.day, month_names[
             dt.month], dt.year, dt.hour, dt.minute, dt.second)

+ 1 - 6
lib/python/temporal/extract.py

@@ -210,12 +210,7 @@ def extract_dataset(input, output, type, where, expression, base, nprocs=1,
                                 continue
 
                     # Set the time stamp
-                    if old_map.is_time_absolute():
-                        start, end, tz = old_map.get_absolute_time()
-                        new_map.set_absolute_time(start, end, tz)
-                    else:
-                        start, end, unit = old_map.get_relative_time()
-                        new_map.set_relative_time(start, end, unit)
+                    new_map.set_temporal_extent(old_map.get_temporal_extent())
 
                     # Insert map in temporal database
                     new_map.insert(dbif)

+ 9 - 9
lib/python/temporal/mapcalc.py

@@ -238,8 +238,8 @@ def dataset_mapcalculator(inputs, output, type, expression, base, method,
 
             # Set the time stamp
             if sample_map_list[i].is_time_absolute():
-                start, end, tz = sample_map_list[i].get_absolute_time()
-                new_map.set_absolute_time(start, end, tz)
+                start, end = sample_map_list[i].get_absolute_time()
+                new_map.set_absolute_time(start, end)
             else:
                 start, end, unit = sample_map_list[i].get_relative_time()
                 new_map.set_relative_time(start, end, unit)
@@ -422,7 +422,7 @@ def _parse_start_operators(expr, is_time_absolute, current):
        * start_second() - The second of the start time [0 - 59]
     """
 
-    start, end, tz = current.get_absolute_time()
+    start, end = current.get_absolute_time()
     msgr = get_tgis_message_interface()
 
     if expr.find("start_year()") >= 0:
@@ -505,7 +505,7 @@ def _parse_end_operators(expr, is_time_absolute, current):
        null()
     """
 
-    start, end, tz = current.get_absolute_time()
+    start, end = current.get_absolute_time()
     msgr = get_tgis_message_interface()
 
     if expr.find("end_year()") >= 0:
@@ -608,7 +608,7 @@ def _parse_td_operator(expr, is_time_absolute, first, current):
     if expr.find("td()") >= 0:
         td = "null()"
         if is_time_absolute:
-            start, end, tz = current.get_absolute_time()
+            start, end = current.get_absolute_time()
             if end != None:
                 td = time_delta_to_relative_time(end - start)
         else:
@@ -630,8 +630,8 @@ def _parse_start_time_operator(expr, is_time_absolute, first, current):
     time, and in relative units in case of relative time."""
     if expr.find("start_time()") >= 0:
         if is_time_absolute:
-            start1, end, tz = first.get_absolute_time()
-            start, end, tz = current.get_absolute_time()
+            start1, end = first.get_absolute_time()
+            start, end = current.get_absolute_time()
             x = time_delta_to_relative_time(start - start1)
         else:
             start1, end, unit = first.get_relative_time()
@@ -656,8 +656,8 @@ def _parse_end_time_operator(expr, is_time_absolute, first, current):
     if expr.find("end_time()") >= 0:
         x = "null()"
         if is_time_absolute:
-            start1, end, tz = first.get_absolute_time()
-            start, end, tz = current.get_absolute_time()
+            start1, end = first.get_absolute_time()
+            start, end = current.get_absolute_time()
             if end:
                 x = time_delta_to_relative_time(end - start1)
         else:

+ 29 - 68
lib/python/temporal/metadata.py

@@ -271,8 +271,7 @@ class RasterMetadata(RasterMetadataBase):
 
         The metadata includes the datatype, number of cols, rows and cells and
         the north-south and east west resolution of the map. Additionally the
-        minimum and maximum values and the name of the space time raster dataset
-        register table is stored.
+        minimum and maximum valuesare stored.
 
         Usage:
 
@@ -298,7 +297,6 @@ class RasterMetadata(RasterMetadataBase):
         0.0
         >>> meta.max
         100.0
-        >>> meta.strds_register
         >>> meta.print_info()
          +-------------------- Metadata information ----------------------------------+
          | Datatype:................... CELL
@@ -309,7 +307,6 @@ class RasterMetadata(RasterMetadataBase):
          | East-west resolution:....... 0.1
          | Minimum value:.............. 0.0
          | Maximum value:.............. 100.0
-         | STRDS register table ....... None
         >>> meta.print_shell_info()
         datatype=CELL
         cols=100
@@ -319,11 +316,10 @@ class RasterMetadata(RasterMetadataBase):
         ewres=0.1
         min=0.0
         max=100.0
-        strds_register=None
 
         @endcode
     """
-    def __init__(self, ident=None, strds_register=None, datatype=None,
+    def __init__(self, ident=None, datatype=None,
 		 cols=None, rows=None, number_of_cells=None, nsres=None,
 		 ewres=None, min=None, max=None):
 
@@ -331,34 +327,15 @@ class RasterMetadata(RasterMetadataBase):
                                       cols, rows, number_of_cells, nsres,
                                       ewres, min, max)
 
-        self.set_strds_register(strds_register)
-
-    def set_strds_register(self, strds_register):
-        """!Set the space time raster dataset register table name"""
-        self.D["strds_register"] = strds_register
-
-    def get_strds_register(self):
-        """!Get the space time raster dataset register table name
-           @return None if not found"""
-        if "strds_register" in self.D:
-            return self.D["strds_register"]
-        else:
-            return None
-
-    strds_register = property(fget=get_strds_register, fset=set_strds_register)
-
     def print_info(self):
         """!Print information about this class in human readable style"""
         print " +-------------------- Metadata information ----------------------------------+"
         #      0123456789012345678901234567890
         RasterMetadataBase.print_info(self)
-        print " | STRDS register table ....... " + str(
-            self.get_strds_register())
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         RasterMetadataBase.print_shell_info(self)
-        print "strds_register=" + str(self.get_strds_register())
 
 ###############################################################################
 
@@ -403,7 +380,6 @@ class Raster3DMetadata(RasterMetadataBase):
         0.0
         >>> meta.max
         100.0
-        >>> meta.str3ds_register
         >>> meta.print_info()
          +-------------------- Metadata information ----------------------------------+
          | Datatype:................... FCELL
@@ -416,7 +392,6 @@ class Raster3DMetadata(RasterMetadataBase):
          | Maximum value:.............. 100.0
          | Number of depths:........... 100
          | Top-Bottom resolution:...... 0.1
-         | STR3DS register table ...... None
         >>> meta.print_shell_info()
         datatype=FCELL
         cols=100
@@ -426,13 +401,12 @@ class Raster3DMetadata(RasterMetadataBase):
         ewres=0.1
         min=0.0
         max=100.0
-        str3ds_register=None
         depths=100
         tbres=0.1
 
         @endcode
     """
-    def __init__(self, ident=None, str3ds_register=None, datatype=None,
+    def __init__(self, ident=None, datatype=None,
 		 cols=None, rows=None, depths=None, number_of_cells=None,
 		 nsres=None, ewres=None, tbres=None, min=None, max=None):
 
@@ -440,14 +414,9 @@ class Raster3DMetadata(RasterMetadataBase):
 				datatype, cols, rows, number_of_cells, nsres,
 				ewres, min, max)
 
-        self.set_str3ds_register(str3ds_register)
         self.set_tbres(tbres)
         self.set_depths(depths)
 
-    def set_str3ds_register(self, str3ds_register):
-        """!Set the space time raster3d dataset register table name"""
-        self.D["str3ds_register"] = str3ds_register
-
     def set_depths(self, depths):
         """!Set the number of depths"""
         if depths is not None:
@@ -462,14 +431,6 @@ class Raster3DMetadata(RasterMetadataBase):
         else:
             self.D["tbres"] = None
 
-    def get_str3ds_register(self):
-        """!Get the space time raster3d dataset register table name
-           @return None if not found"""
-        if "str3ds_register" in self.D:
-            return self.D["str3ds_register"]
-        else:
-            return None
-
     def get_depths(self):
         """!Get number of depths
            @return None if not found"""
@@ -488,7 +449,6 @@ class Raster3DMetadata(RasterMetadataBase):
 
     depths = property(fget=get_depths, fset=set_depths)
     tbres = property(fget=get_tbres, fset=set_tbres)
-    str3ds_register = property(fget=get_str3ds_register, fset=set_str3ds_register)
 
     def print_info(self):
         """!Print information about this class in human readable style"""
@@ -498,13 +458,10 @@ class Raster3DMetadata(RasterMetadataBase):
         #      0123456789012345678901234567890
         print " | Number of depths:........... " + str(self.get_depths())
         print " | Top-Bottom resolution:...... " + str(self.get_tbres())
-        print " | STR3DS register table ...... " + str(
-                                                self.get_str3ds_register())
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         RasterMetadataBase.print_shell_info(self)
-        print "str3ds_register=" + str(self.get_str3ds_register())
         print "depths=" + str(self.get_depths())
         print "tbres=" + str(self.get_tbres())
 
@@ -558,7 +515,6 @@ class VectorMetadata(SQLDatabaseInterface):
         12
         >>> meta.print_info()
          +-------------------- Metadata information ----------------------------------+
-         | STVDS register table ....... None
          | Is map 3d .................. True
          | Number of points ........... 1
          | Number of lines ............ 2
@@ -573,7 +529,6 @@ class VectorMetadata(SQLDatabaseInterface):
          | Number of holes ............ 11
          | Number of volumes .......... 12
         >>> meta.print_shell_info()
-        stvds_register=None
         is_3d=True
         points=1
         lines=2
@@ -591,7 +546,7 @@ class VectorMetadata(SQLDatabaseInterface):
         @endcode
     """
     def __init__(
-        self, ident=None, stvds_register=None, is_3d=False,
+        self, ident=None, is_3d=False,
         number_of_points=None, number_of_lines=None, number_of_boundaries=None,
         number_of_centroids=None, number_of_faces=None, number_of_kernels=None,
         number_of_primitives=None, number_of_nodes=None, number_of_areas=None,
@@ -600,7 +555,6 @@ class VectorMetadata(SQLDatabaseInterface):
         SQLDatabaseInterface.__init__(self, "vector_metadata", ident)
 
         self.set_id(ident)
-        self.set_stvds_register(stvds_register)
         self.set_3d_info(is_3d)
         self.set_number_of_points(number_of_points)
         self.set_number_of_lines(number_of_lines)
@@ -620,10 +574,6 @@ class VectorMetadata(SQLDatabaseInterface):
         self.ident = ident
         self.D["id"] = ident
 
-    def set_stvds_register(self, stvds_register):
-        """!Set the space time vector dataset register table name"""
-        self.D["stvds_register"] = stvds_register
-
     def set_3d_info(self, is_3d):
         """!Set True if the vector map is three dimensional"""
         self.D["is_3d"] = is_3d
@@ -685,14 +635,6 @@ class VectorMetadata(SQLDatabaseInterface):
         else:
             return None
 
-    def get_stvds_register(self):
-        """!Get the space time vector dataset register table name
-           @return None if not found"""
-        if "stvds_register" in self.D:
-            return self.D["stvds_register"]
-        else:
-            return None
-
     def get_3d_info(self):
         """!Return True if the map is three dimensional,
            False if not and None if not info was found"""
@@ -799,8 +741,6 @@ class VectorMetadata(SQLDatabaseInterface):
 
     # Set the properties
     id  = property(fget=get_id, fset=set_id)
-    stvds_register  = property(fget=get_stvds_register,
-                               fset=set_stvds_register)
     is_3d  = property(fget=get_3d_info, fset=set_3d_info)
     number_of_points = property(fget=get_number_of_points,
                                 fset=set_number_of_points)
@@ -831,8 +771,6 @@ class VectorMetadata(SQLDatabaseInterface):
         """!Print information about this class in human readable style"""
         #      0123456789012345678901234567890
         print " +-------------------- Metadata information ----------------------------------+"
-        print " | STVDS register table ....... " + str(
-            self.get_stvds_register())
         print " | Is map 3d .................. " + str(self.get_3d_info())
         print " | Number of points ........... " + str(self.get_number_of_points())
         print " | Number of lines ............ " + str(self.get_number_of_lines())
@@ -849,7 +787,6 @@ class VectorMetadata(SQLDatabaseInterface):
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
-        print "stvds_register=" + str(self.get_stvds_register())
         print "is_3d=" + str(self.get_3d_info())
         print "points=" + str(self.get_number_of_points())
         print "lines=" + str(self.get_number_of_lines())
@@ -1068,6 +1005,7 @@ class STDSRasterMetadataBase(STDSMetadataBase):
          | Minimum value max:.......... None
          | Maximum value min:.......... None
          | Maximum value max:.......... None
+         | Aggregation type:........... None
          | Number of registered maps:.. None
          |
          | Title:
@@ -1076,6 +1014,7 @@ class STDSRasterMetadataBase(STDSMetadataBase):
          | Soils 1950 - 2010
          | Command history:
         >>> meta.print_shell_info()
+        aggregation_type=None
         number_of_maps=None
         nsres_min=None
         nsres_max=None
@@ -1088,7 +1027,7 @@ class STDSRasterMetadataBase(STDSMetadataBase):
 
         @endcode
     """
-    def __init__(self, table=None, ident=None, title=None, description=None):
+    def __init__(self, table=None, ident=None, title=None, description=None, aggregation_type=None):
 
         STDSMetadataBase.__init__(self, table, ident, title, description)
 
@@ -1101,7 +1040,21 @@ class STDSRasterMetadataBase(STDSMetadataBase):
         self.D["nsres_max"] = None
         self.D["ewres_min"] = None
         self.D["ewres_max"] = None
+        self.D["aggregation_type"] = aggregation_type
 
+    def set_aggregation_type(self, aggregation_type):
+        """!Set the aggregation type of the dataset (mean, min, max, ...)"""
+        self.D["aggregation_type"] = aggregation_type
+
+    def get_aggregation_type(self):
+        """!Get the aggregation type of the dataset (mean, min, max, ...)
+           @return None if not found
+        """
+        if "aggregation_type" in self.D:
+            return self.D["aggregation_type"]
+        else:
+            return None
+            
     def get_max_min(self):
         """!Get the minimal maximum of all registered maps,
            this value is set in the database
@@ -1190,6 +1143,8 @@ class STDSRasterMetadataBase(STDSMetadataBase):
     min_max = property(fget=get_min_max)
     max_min = property(fget=get_max_min)
     max_max = property(fget=get_max_max)
+    aggregation_type = property(fset=set_aggregation_type, 
+                                fget=get_aggregation_type)
 
     def print_info(self):
         """!Print information about this class in human readable style"""
@@ -1202,10 +1157,12 @@ class STDSRasterMetadataBase(STDSMetadataBase):
         print " | Minimum value max:.......... " + str(self.get_min_max())
         print " | Maximum value min:.......... " + str(self.get_max_min())
         print " | Maximum value max:.......... " + str(self.get_max_max())
+        print " | Aggregation type:........... " + str(self.get_aggregation_type())
         STDSMetadataBase.print_info(self)
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
+        print "aggregation_type=" + str(self.get_aggregation_type())
         STDSMetadataBase.print_shell_info(self)
         print "nsres_min=" + str(self.get_nsres_min())
         print "nsres_max=" + str(self.get_nsres_max())
@@ -1264,6 +1221,7 @@ class STRDSMetadata(STDSRasterMetadataBase):
          | Minimum value max:.......... None
          | Maximum value min:.......... None
          | Maximum value max:.......... None
+         | Aggregation type:........... None
          | Number of registered maps:.. None
          |
          | Title:
@@ -1272,6 +1230,7 @@ class STRDSMetadata(STDSRasterMetadataBase):
          | Soils 1950 - 2010
          | Command history:
         >>> meta.print_shell_info()
+        aggregation_type=None
         number_of_maps=None
         nsres_min=None
         nsres_max=None
@@ -1372,6 +1331,7 @@ class STR3DSMetadata(STDSRasterMetadataBase):
          | Minimum value max:.......... None
          | Maximum value min:.......... None
          | Maximum value max:.......... None
+         | Aggregation type:........... None
          | Number of registered maps:.. None
          |
          | Title:
@@ -1380,6 +1340,7 @@ class STR3DSMetadata(STDSRasterMetadataBase):
          | Soils 1950 - 2010
          | Command history:
         >>> meta.print_shell_info()
+        aggregation_type=None
         number_of_maps=None
         nsres_min=None
         nsres_max=None

+ 4 - 3
lib/python/temporal/register.py

@@ -239,9 +239,10 @@ def register_maps_in_space_time_dataset(
 
             # Save the datasets that must be updated
             datasets = map.get_registered_datasets(dbif)
-            if datasets:
+            if datasets is not None:
                 for dataset in datasets:
-                    datatsets_to_modify[dataset["id"]] = dataset["id"]
+                    if dataset != "":
+                        datatsets_to_modify[dataset] = dataset
 
                 if name and map.get_temporal_type() != sp.get_temporal_type():
                     dbif.close()
@@ -398,7 +399,7 @@ def assign_valid_time_to_map(ttype, map, start, end, unit, increment=None,
             msgr.debug(1, _("Set absolute valid time for map <%s> to %s - %s") %
                          (map.get_map_id(), str(start_time), str(end_time)))
 
-        map.set_absolute_time(start_time, end_time, None)
+        map.set_absolute_time(start_time, end_time)
     else:
         start_time = int(start)
         end_time = None

+ 51 - 42
lib/python/temporal/space_time_datasets.py

@@ -73,7 +73,6 @@ class RasterDataset(AbstractMapDataset):
          | East-west resolution:....... 10.0
          | Minimum value:.............. 1.0
          | Maximum value:.............. 1.0
-         | STRDS register table ....... None
 
         >>> newmap = rmap.get_new_instance("new@PERMANENT")
         >>> isinstance(newmap, RasterDataset)
@@ -83,12 +82,11 @@ class RasterDataset(AbstractMapDataset):
         True
         >>> rmap.get_type()
         'raster'
-        >>> rmap.get_stds_register()
         >>> rmap.set_absolute_time(start_time=datetime(2001,1,1),
         ...                        end_time=datetime(2012,1,1))
         True
         >>> rmap.get_absolute_time()
-        (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
+        (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> rmap.get_temporal_extent_as_tuple()
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> rmap.get_name()
@@ -114,6 +112,13 @@ class RasterDataset(AbstractMapDataset):
         AbstractMapDataset.__init__(self)
         self.reset(ident)
 
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+        return False
+        
     def get_type(self):
         return 'raster'
 
@@ -126,16 +131,6 @@ class RasterDataset(AbstractMapDataset):
         are stored with the type of this class"""
         return SpaceTimeRasterDataset(ident)
 
-    def get_stds_register(self):
-        """!Return the space time dataset register table name in which stds
-        are listed in which this map is registered"""
-        return self.metadata.get_strds_register()
-
-    def set_stds_register(self, name):
-        """!Set the space time dataset register table name in which stds
-        are listed in which this map is registered"""
-        self.metadata.set_strds_register(name)
-
     def spatial_overlapping(self, dataset):
         """!Return True if the spatial extents 2d overlap"""
         return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
@@ -199,6 +194,7 @@ class RasterDataset(AbstractMapDataset):
         self.relative_time = RasterRelativeTime(ident=ident)
         self.spatial_extent = RasterSpatialExtent(ident=ident)
         self.metadata = RasterMetadata(ident=ident)
+        self.stds_register = RasterSTDSRegister(ident=ident)
 
     def has_grass_timestamp(self):
         """!Check if a grass file bsased time stamp exists for this map.
@@ -229,7 +225,7 @@ class RasterDataset(AbstractMapDataset):
             return False
 
         if len(dates) == 2:
-            self.set_absolute_time(dates[0], dates[1], None)
+            self.set_absolute_time(dates[0], dates[1])
         else:
             self.set_relative_time(dates[0], dates[1], dates[2])
 
@@ -380,7 +376,6 @@ class Raster3DDataset(AbstractMapDataset):
          | Maximum value:.............. 1.0
          | Number of depths:........... 10
          | Top-Bottom resolution:...... 10.0
-         | STR3DS register table ...... None
 
         >>> newmap = r3map.get_new_instance("new@PERMANENT")
         >>> isinstance(newmap, Raster3DDataset)
@@ -390,12 +385,11 @@ class Raster3DDataset(AbstractMapDataset):
         True
         >>> r3map.get_type()
         'raster3d'
-        >>> r3map.get_stds_register()
         >>> r3map.set_absolute_time(start_time=datetime(2001,1,1),
         ...                        end_time=datetime(2012,1,1))
         True
         >>> r3map.get_absolute_time()
-        (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
+        (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> r3map.get_temporal_extent_as_tuple()
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> r3map.get_name()
@@ -418,6 +412,13 @@ class Raster3DDataset(AbstractMapDataset):
         AbstractMapDataset.__init__(self)
         self.reset(ident)
 
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+        return False
+        
     def get_type(self):
         return "raster3d"
 
@@ -430,16 +431,6 @@ class Raster3DDataset(AbstractMapDataset):
         are stored with the type of this class"""
         return SpaceTimeRaster3DDataset(ident)
 
-    def get_stds_register(self):
-        """!Return the space time dataset register table name in
-        which stds are listed in which this map is registered"""
-        return self.metadata.get_str3ds_register()
-
-    def set_stds_register(self, name):
-        """!Set the space time dataset register table name in
-        which stds are listed in which this map is registered"""
-        self.metadata.set_str3ds_register(name)
-
     def spatial_overlapping(self, dataset):
         """!Return True if the spatial extents overlap"""
         if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
@@ -518,6 +509,7 @@ class Raster3DDataset(AbstractMapDataset):
         self.relative_time = Raster3DRelativeTime(ident=ident)
         self.spatial_extent = Raster3DSpatialExtent(ident=ident)
         self.metadata = Raster3DMetadata(ident=ident)
+        self.stds_register = Raster3DSTDSRegister(ident=ident)
 
     def has_grass_timestamp(self):
         """!Check if a grass file bsased time stamp exists for this map.
@@ -548,7 +540,7 @@ class Raster3DDataset(AbstractMapDataset):
             return False
 
         if len(dates) == 2:
-            self.set_absolute_time(dates[0], dates[1], None)
+            self.set_absolute_time(dates[0], dates[1])
         else:
             self.set_relative_time(dates[0], dates[1], dates[2])
 
@@ -679,7 +671,6 @@ class VectorDataset(AbstractMapDataset):
          | End time:................... None
         >>> vmap.metadata.print_info()
          +-------------------- Metadata information ----------------------------------+
-         | STVDS register table ....... None
          | Is map 3d .................. True
          | Number of points ........... 100
          | Number of lines ............ 0
@@ -701,12 +692,11 @@ class VectorDataset(AbstractMapDataset):
         True
         >>> vmap.get_type()
         'vector'
-        >>> vmap.get_stds_register()
         >>> vmap.set_absolute_time(start_time=datetime(2001,1,1),
         ...                        end_time=datetime(2012,1,1))
         True
         >>> vmap.get_absolute_time()
-        (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0), None)
+        (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> vmap.get_temporal_extent_as_tuple()
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> vmap.get_name()
@@ -729,6 +719,13 @@ class VectorDataset(AbstractMapDataset):
         AbstractMapDataset.__init__(self)
         self.reset(ident)
 
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+        return False
+        
     def get_type(self):
         return "vector"
 
@@ -741,16 +738,6 @@ class VectorDataset(AbstractMapDataset):
         are stored with the type of this class"""
         return SpaceTimeVectorDataset(ident)
 
-    def get_stds_register(self):
-        """!Return the space time dataset register table name in
-        which stds are listed in which this map is registered"""
-        return self.metadata.get_stvds_register()
-
-    def set_stds_register(self, name):
-        """!Set the space time dataset register table name in
-        which stds are listed in which this map is registered"""
-        self.metadata.set_stvds_register(name)
-
     def get_layer(self):
         """!Return the layer"""
         return self.base.get_layer()
@@ -798,6 +785,7 @@ class VectorDataset(AbstractMapDataset):
         self.relative_time = VectorRelativeTime(ident=ident)
         self.spatial_extent = VectorSpatialExtent(ident=ident)
         self.metadata = VectorMetadata(ident=ident)
+        self.stds_register = VectorSTDSRegister(ident=ident)
 
     def has_grass_timestamp(self):
         """!Check if a grass file bsased time stamp exists for this map.
@@ -826,7 +814,7 @@ class VectorDataset(AbstractMapDataset):
             return False
 
         if len(dates) == 2:
-            self.set_absolute_time(dates[0], dates[1], None)
+            self.set_absolute_time(dates[0], dates[1])
         else:
             self.set_relative_time(dates[0], dates[1], dates[2])
 
@@ -920,6 +908,13 @@ class SpaceTimeRasterDataset(AbstractSpaceTimeDataset):
     def __init__(self, ident):
         AbstractSpaceTimeDataset.__init__(self, ident)
 
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+        return True
+        
     def get_type(self):
         return "strds"
 
@@ -993,6 +988,13 @@ class SpaceTimeRaster3DDataset(AbstractSpaceTimeDataset):
     def __init__(self, ident):
         AbstractSpaceTimeDataset.__init__(self, ident)
 
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+        return True
+        
     def get_type(self):
         return "str3ds"
 
@@ -1085,6 +1087,13 @@ class SpaceTimeVectorDataset(AbstractSpaceTimeDataset):
     def __init__(self, ident):
         AbstractSpaceTimeDataset.__init__(self, ident)
 
+    def is_stds(self):
+        """!Return True if this class is a space time dataset
+
+           @return True if this class is a space time dataset, False otherwise
+        """
+        return True
+        
     def get_type(self):
         return "stvds"
 

+ 18 - 47
lib/python/temporal/temporal_extent.py

@@ -198,8 +198,7 @@ class AbstractTemporalExtent(SQLDatabaseInterface):
             return RelativeTemporalExtent(start_time=start, end_time=end,
                                           unit=self.get_unit())
         elif issubclass(type(self), AbsoluteTemporalExtent):
-            return AbsoluteTemporalExtent(start_time=start, end_time=end,
-                                          timezone=self.get_timezone())
+            return AbsoluteTemporalExtent(start_time=start, end_time=end)
         elif issubclass(type(self), AbstractTemporalExtent):
             return AbstractTemporalExtent(start_time=start, end_time=end)
 
@@ -384,8 +383,7 @@ class AbstractTemporalExtent(SQLDatabaseInterface):
             return RelativeTemporalExtent(start_time=start, end_time=end,
                                           unit=self.get_unit())
         elif issubclass(type(self), AbsoluteTemporalExtent):
-            return AbsoluteTemporalExtent(start_time=start, end_time=end,
-                                          timezone=self.get_timezone())
+            return AbsoluteTemporalExtent(start_time=start, end_time=end)
         elif issubclass(type(self), AbstractTemporalExtent):
             return AbstractTemporalExtent(start_time=start, end_time=end)
 
@@ -1053,64 +1051,37 @@ class AbsoluteTemporalExtent(AbstractTemporalExtent):
 
         start_time and end_time must be of type datetime
     """
-    def __init__(self, table=None, ident=None, start_time=None, end_time=None,
-                 timezone=None):
+    def __init__(self, table=None, ident=None, start_time=None, end_time=None):
 
         AbstractTemporalExtent.__init__(
             self, table, ident, start_time, end_time)
 
-        self.set_timezone(timezone)
-
-    def set_timezone(self, timezone):
-        """!Set the timezone of the map, the timezone is of type string.
-           Timezones are not supported yet, instead the timezone
-           is set in the datetime string as offset in minutes.
-        """
-        self.D["timezone"] = timezone
-
-    def get_timezone(self):
-        """!Get the timezone of the map
-           Timezones are not supported yet, instead the timezone
-           is set in the datetime string as offset in minutes.
-           @return None if not found"""
-        if "timezone" in self.D:
-            return self.D["timezone"]
-        else:
-            return None
-
-    timezone = property(fget=get_timezone, fset=set_timezone)
-
     def print_info(self):
         """!Print information about this class in human readable style"""
         #      0123456789012345678901234567890
         print " +-------------------- Absolute time -----------------------------------------+"
         AbstractTemporalExtent.print_info(self)
-        #print " | Timezone:................... " + str(self.get_timezone())
 
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         AbstractTemporalExtent.print_shell_info(self)
-        #print "timezone=" + str(self.get_timezone())
 
 ###############################################################################
 
 class RasterAbsoluteTime(AbsoluteTemporalExtent):
-    def __init__(self, ident=None, start_time=None, end_time=None,
-                 timezone=None):
+    def __init__(self, ident=None, start_time=None, end_time=None):
         AbsoluteTemporalExtent.__init__(self, "raster_absolute_time",
-            ident, start_time, end_time, timezone)
+            ident, start_time, end_time)
 
 class Raster3DAbsoluteTime(AbsoluteTemporalExtent):
-    def __init__(self, ident=None, start_time=None, end_time=None,
-                 timezone=None):
+    def __init__(self, ident=None, start_time=None, end_time=None):
         AbsoluteTemporalExtent.__init__(self, "raster3d_absolute_time",
-            ident, start_time, end_time, timezone)
+            ident, start_time, end_time)
 
 class VectorAbsoluteTime(AbsoluteTemporalExtent):
-    def __init__(self, ident=None, start_time=None, end_time=None,
-                 timezone=None):
+    def __init__(self, ident=None, start_time=None, end_time=None):
         AbsoluteTemporalExtent.__init__(self, "vector_absolute_time",
-            ident, start_time, end_time, timezone)
+            ident, start_time, end_time)
 
 ###############################################################################
 
@@ -1154,9 +1125,9 @@ class STDSAbsoluteTime(AbsoluteTemporalExtent):
         @endcode
     """
     def __init__(self, table=None, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None, map_time=None):
+                 granularity=None, map_time=None):
         AbsoluteTemporalExtent.__init__(
-            self, table, ident, start_time, end_time, timezone)
+            self, table, ident, start_time, end_time)
 
         self.set_granularity(granularity)
         self.set_map_time(map_time)
@@ -1221,23 +1192,23 @@ class STDSAbsoluteTime(AbsoluteTemporalExtent):
 
 class STRDSAbsoluteTime(STDSAbsoluteTime):
     def __init__(self, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None):
+                 granularity=None):
         STDSAbsoluteTime.__init__(self, "strds_absolute_time",
-            ident, start_time, end_time, granularity, timezone)
+            ident, start_time, end_time, granularity)
 
 
 class STR3DSAbsoluteTime(STDSAbsoluteTime):
     def __init__(self, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None):
+                 granularity=None):
         STDSAbsoluteTime.__init__(self, "str3ds_absolute_time",
-            ident, start_time, end_time, granularity, timezone)
+            ident, start_time, end_time, granularity)
 
 
 class STVDSAbsoluteTime(STDSAbsoluteTime):
     def __init__(self, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None):
+                 granularity=None):
         STDSAbsoluteTime.__init__(self, "stvds_absolute_time",
-            ident, start_time, end_time, granularity, timezone)
+            ident, start_time, end_time, granularity)
 
 ###############################################################################
 
@@ -1278,7 +1249,7 @@ class RelativeTemporalExtent(AbstractTemporalExtent):
 
         AbstractTemporalExtent.__init__(
             self, table, ident, start_time, end_time)
-        self.D["unit"] = unit
+        self.set_unit(unit)
 
     def set_unit(self, unit):
         """!Set the unit of the relative time. Valid units are:

+ 10 - 8
lib/python/temporal/unittests_register.py

@@ -20,7 +20,7 @@ class TestRegisterFunctions(unittest.TestCase):
     def setUpClass(cls):
         """!Initiate the temporal GIS and set the region
         """
-        tgis.init(True)
+        tgis.init()
         grass.overwrite = True
         grass.use_temp_region()
         ret = grass.run_command("g.region", n=80.0, s=0.0, e=120.0, 
@@ -51,19 +51,19 @@ class TestRegisterFunctions(unittest.TestCase):
 
         map = tgis.RasterDataset("register_map_1@" + tgis.get_current_mapset())
         map.select()
-        start, end, tz = map.get_absolute_time()
+        start, end = map.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 1))
         self.assertEqual(end, datetime.datetime(2001, 1, 2))
 
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map.select()
-        start, end, tz = map.get_absolute_time()
+        start, end = map.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 2))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
 
         strds = tgis.SpaceTimeRasterDataset("register_test@" + tgis.get_current_mapset())
         strds.select()
-        start, end, tz = strds.get_absolute_time()
+        start, end = strds.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 1))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
 
@@ -79,13 +79,13 @@ class TestRegisterFunctions(unittest.TestCase):
 
         map = tgis.RasterDataset("register_map_1@" + tgis.get_current_mapset())
         map.select()
-        start, end, tz = map.get_absolute_time()
+        start, end = map.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 1))
         self.assertEqual(end, datetime.datetime(2001, 1, 2))
 
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map.select()
-        start, end, tz = map.get_absolute_time()
+        start, end = map.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 2))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
 
@@ -98,12 +98,12 @@ class TestRegisterFunctions(unittest.TestCase):
 
         map = tgis.RasterDataset("register_map_1@" + tgis.get_current_mapset())
         map.select()
-        start, end, tz = map.get_absolute_time()
+        start, end = map.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 1, 10, 30, 1))
 
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map.select()
-        start, end, tz = map.get_absolute_time()
+        start, end = map.get_absolute_time()
         self.assertEqual(start, datetime.datetime(2001, 1, 1, 18, 30, 1))
 
     def test_relative_time_strds(self):
@@ -140,6 +140,8 @@ class TestRegisterFunctions(unittest.TestCase):
         self.assertEqual(start, 0)
         self.assertEqual(end, 2)
         self.assertEqual(unit, "day")
+        
+        strds.print_info()
 
         ret = grass.run_command("t.remove", input="register_test") 
         self.assertEqual(ret, 0)

+ 0 - 12
lib/temporal/SQL/map_stds_register_table_template.sql

@@ -1,23 +0,0 @@
---#############################################################################
---
---
---#############################################################################
-
---PRAGMA foreign_keys = ON;
-
-
-CREATE TABLE  TABLE_NAME_STDS_register (
-  id VARCHAR NOT NULL, -- This column is a primary foreign key storing the STDS names
-  FOREIGN KEY (id) REFERENCES  STDS_base (id) ON DELETE CASCADE ON UPDATE CASCADE
-);

+ 13 - 15
lib/temporal/SQL/map_tables_template.sql

@@ -12,7 +12,6 @@
 
 -- GRASS_MAP is a placeholder for specific map type: raster, raster3d or vector
 
---PRAGMA foreign_keys = ON;
 
 CREATE TABLE  GRASS_MAP_base (
   id VARCHAR NOT NULL,                  -- The id (PK) is the unique identifier for all tables, it is based on name (layer) and mapset (name(:layer)@mapset) and is used as primary key
@@ -22,39 +21,29 @@ CREATE TABLE  GRASS_MAP_base (
   creator VARCHAR NOT NULL,
   temporal_type VARCHAR,                -- The temporal type of the grass map "absolute" or "relative" or NULL in case no time stamp is available
   creation_time TIMESTAMP NOT NULL,      -- The time of creation of the grass map
   PRIMARY KEY (id)
 );
 
-CREATE INDEX GRASS_MAP_base_index ON GRASS_MAP_base (id);
-
 -- Relative valid time interval with start and end time
 CREATE TABLE  GRASS_MAP_relative_time (
-  id VARCHAR NOT NULL,          -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreign key
+  id VARCHAR NOT NULL,          -- The id (PK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreign key
   start_time INTEGER,  -- The relative valid start time in 
   end_time INTEGER,    -- The relative valid end time in 
   unit VARCHAR,                 -- The relative time unit, available are "years, months, days, minutes, seconds"
-  FOREIGN KEY (id) REFERENCES  GRASS_MAP_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
-CREATE INDEX GRASS_MAP_relative_time_index ON GRASS_MAP_relative_time (id, start_time, end_time);
-
 CREATE TABLE  GRASS_MAP_absolute_time (
-  id VARCHAR NOT NULL,   -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreign key
+  id VARCHAR NOT NULL,   -- The id (PK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary key
   start_time TIMESTAMP,  --  Start of the valid time, can be NULL if no time information is available
   end_time TIMESTAMP,    --  End of the valid time, can be NULL if no time information is available or valid time is a single point in time
-  timezone VARCHAR,      -- The timezone of the valid time stored as string. This is currently not in use. Instead the timezone is set in the datetime strings 
-  FOREIGN KEY (id) REFERENCES  GRASS_MAP_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
-CREATE INDEX GRASS_MAP_absolute_time_index ON GRASS_MAP_absolute_time (id, start_time, end_time);
-
 -- The spatial extent of a raster map
 
 CREATE TABLE  GRASS_MAP_spatial_extent (
-  id VARCHAR NOT NULL,                  -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreigen key
+  id VARCHAR NOT NULL,                  -- The id (PK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary key
   -- below is the spatial extent of the map
   north DOUBLE PRECISION NOT NULL,
   south DOUBLE PRECISION NOT NULL,
@@ -63,7 +52,13 @@ CREATE TABLE  GRASS_MAP_spatial_extent (
   top DOUBLE PRECISION NOT NULL,
   bottom DOUBLE PRECISION NOT NULL,
   proj VARCHAR,
-  FOREIGN KEY (id) REFERENCES  GRASS_MAP_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
-CREATE INDEX GRASS_MAP_spatial_extent_index ON GRASS_MAP_spatial_extent (id);
+-- We have a specific table that stores the space time dataset ids in which the maps a registered
+
+CREATE TABLE  GRASS_MAP_stds_register (
+  id VARCHAR NOT NULL,                  -- The id (PK) is the unique identifier for all tables, it is based on name (layer) and mapset (name(:layer)@mapset) and is used as primary key
+  registered_stds VARCHAR,              -- This column stores the names of all space time datasets in which a specific map is registered
+  PRIMARY KEY (id)
+);

+ 77 - 0
lib/temporal/SQL/postgresql_delete_trigger.sql

@@ -0,0 +1,77 @@
+--#############################################################################
+-- Create trigger for automated deletion of dependent rows
+--
+-- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
+--#############################################################################
+
+CREATE OR REPLACE FUNCTION delete_strds_base() RETURNS TRIGGER AS $$
+BEGIN
+DELETE FROM strds_absolute_time WHERE id = OLD.id;
+DELETE FROM strds_relative_time WHERE id = OLD.id;
+DELETE FROM strds_spatial_extent WHERE id = OLD.id;
+DELETE FROM strds_metadata WHERE id = OLD.id;
+RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_raster_base() RETURNS TRIGGER AS $$
+BEGIN
+DELETE FROM raster_absolute_time WHERE id = OLD.id;
+DELETE FROM raster_relative_time WHERE id = OLD.id;
+DELETE FROM raster_spatial_extent WHERE id = OLD.id;
+DELETE FROM raster_metadata WHERE id = OLD.id;
+DELETE FROM raster_stds_register WHERE id = OLD.id;
+RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_str3ds_base() RETURNS TRIGGER AS $$
+BEGIN
+DELETE FROM str3ds_absolute_time WHERE id = OLD.id;
+DELETE FROM str3ds_relative_time WHERE id = OLD.id;
+DELETE FROM str3ds_spatial_extent WHERE id = OLD.id;
+DELETE FROM str3ds_metadata WHERE id = OLD.id;
+RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_raster3d_base() RETURNS TRIGGER AS $$
+BEGIN
+DELETE FROM raster3d_absolute_time WHERE id = OLD.id;
+DELETE FROM raster3d_relative_time WHERE id = OLD.id;
+DELETE FROM raster3d_spatial_extent WHERE id = OLD.id;
+DELETE FROM raster3d_metadata WHERE id = OLD.id;
+DELETE FROM raster3d_stds_register WHERE id = OLD.id;
+RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_stvds_base() RETURNS TRIGGER AS $$
+BEGIN
+DELETE FROM stvds_absolute_time WHERE id = OLD.id;
+DELETE FROM stvds_relative_time WHERE id = OLD.id;
+DELETE FROM stvds_spatial_extent WHERE id = OLD.id;
+DELETE FROM stvds_metadata WHERE id = OLD.id;
+RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION delete_vector_base() RETURNS TRIGGER AS $$
+BEGIN
+DELETE FROM vector_absolute_time WHERE id = OLD.id;
+DELETE FROM vector_relative_time WHERE id = OLD.id;
+DELETE FROM vector_spatial_extent WHERE id = OLD.id;
+DELETE FROM vector_metadata WHERE id = OLD.id;
+DELETE FROM vector_stds_register WHERE id = OLD.id;
+RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE TRIGGER delete_strds_base AFTER DELETE ON strds_base FOR EACH ROW EXECUTE PROCEDURE delete_strds_base();
+CREATE TRIGGER delete_raster_base AFTER DELETE ON raster_base FOR EACH ROW EXECUTE PROCEDURE delete_raster_base();
+CREATE TRIGGER delete_str3ds_base AFTER DELETE ON str3ds_base FOR EACH ROW EXECUTE PROCEDURE delete_str3ds_base();
+CREATE TRIGGER delete_raster3d_base AFTER DELETE ON raster3d_base FOR EACH ROW EXECUTE PROCEDURE delete_raster3d_base();
+CREATE TRIGGER delete_stvds_base AFTER DELETE ON stvds_base FOR EACH ROW EXECUTE PROCEDURE delete_stvds_base();
+CREATE TRIGGER delete_vector_base AFTER DELETE ON vector_base FOR EACH ROW EXECUTE PROCEDURE delete_vector_base();
+

+ 14 - 0
lib/temporal/SQL/postgresql_indexes.sql

@@ -0,0 +1,14 @@
+--#############################################################################
+-- This SQL script generates the postgresql indexes
+--
+-- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
+--#############################################################################
+
+CREATE INDEX raster_relative_time_index ON raster_relative_time (start_time, end_time);
+CREATE INDEX raster_absolute_time_index ON raster_absolute_time (start_time, end_time);
+
+CREATE INDEX raster3d_relative_time_index ON raster3d_relative_time (start_time, end_time);
+CREATE INDEX raster3d_absolute_time_index ON raster3d_absolute_time (start_time, end_time);
+
+CREATE INDEX vector_relative_time_index ON vector_relative_time (start_time, end_time);
+CREATE INDEX vector_absolute_time_index ON vector_absolute_time (start_time, end_time);

+ 4 - 5
lib/temporal/SQL/raster3d_metadata_table.sql

@@ -5,13 +5,11 @@
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 
---PRAGMA foreign_keys = ON;
 
 -- The metadata table reflects most of the raster3d metadata available in grass
 
 CREATE TABLE  raster3d_metadata (
-  id VARCHAR NOT NULL,                  -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreign key
-  str3ds_register VARCHAR, -- The name of the table storing all space-time raster3d datasets in which this map is registered
+  id VARCHAR NOT NULL,                  -- The id (PK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary key
   datatype VARCHAR NOT NULL,
   cols INTEGER NOT NULL,
   rows INTEGER NOT NULL,
@@ -22,8 +20,9 @@ CREATE TABLE  raster3d_metadata (
   tbres DOUBLE PRECISION NOT NULL,
   min DOUBLE PRECISION,
   max DOUBLE PRECISION,
-  FOREIGN KEY (id) REFERENCES  raster3d_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
-CREATE INDEX raster3d_metadata_index ON raster3d_metadata (id);
+
+
 

+ 15 - 11
lib/temporal/SQL/raster3d_views.sql

@@ -10,35 +10,35 @@
 CREATE VIEW raster3d_view_abs_time AS SELECT 
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
-            A1.creation_time, 
+            A1.creation_time,  
             A1.creator,
-            A2.start_time, A2.end_time, A2.timezone,
+            A2.start_time, A2.end_time,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows, A4.depths,
             A4.nsres, A4.ewres, A4.tbres,
             A4.min, A4.max,
-            A4.str3ds_register,
-            A4.number_of_cells
+            A4.number_of_cells, A5.registered_stds
             FROM raster3d_base A1, raster3d_absolute_time A2, 
-            raster3d_spatial_extent A3, raster3d_metadata A4 
-            WHERE A1.id = A2.id AND A1.id = A3.id AND A1.id = A4.id;
+            raster3d_spatial_extent A3, raster3d_metadata A4,
+            raster3d_stds_register A5
+            WHERE A1.id = A2.id AND A1.id = A3.id AND 
+            A1.id = A4.id AND A1.id = A5.id;
 
 CREATE VIEW raster3d_view_rel_time AS SELECT 
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.creation_time, 
             A1.creator,
-            A2.start_time, A2.end_time,
+            A2.start_time, A2.end_time, A2.unit,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows, A4.depths,
             A4.nsres, A4.ewres, A4.tbres,
             A4.min, A4.max,
-            A4.str3ds_register,
-            A4.number_of_cells
+            A4.number_of_cells, A5.registered_stds
             FROM raster3d_base A1, raster3d_relative_time A2, 
-            raster3d_spatial_extent A3, raster3d_metadata A4 
-            WHERE A1.id = A2.id AND A1.id = A3.id AND A1.id = A4.id;
+            raster3d_spatial_extent A3, raster3d_metadata A4, 
+            raster3d_stds_register A5
+            WHERE A1.id = A2.id AND A1.id = A3.id AND 
+            A1.id = A4.id AND A1.id = A5.id;
+
+

+ 3 - 4
lib/temporal/SQL/raster_metadata_table.sql

@@ -10,8 +10,7 @@
 -- The metadata table reflects most of the raster metadata available in grass
 
 CREATE TABLE  raster_metadata (
-  id VARCHAR NOT NULL,               -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreign key
-  strds_register VARCHAR,            -- The name of the table storing all space-time raster datasets in which this map is registered
+  id VARCHAR NOT NULL,               -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary key
   datatype VARCHAR NOT NULL,
   cols INTEGER NOT NULL,
   rows INTEGER NOT NULL,
@@ -20,7 +19,7 @@ CREATE TABLE  raster_metadata (
   ewres DOUBLE PRECISION NOT NULL,
   min DOUBLE PRECISION,
   max DOUBLE PRECISION,
-  FOREIGN KEY (id) REFERENCES  raster_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
-CREATE INDEX raster_metadata_index ON raster_metadata (id);
+

+ 14 - 10
lib/temporal/SQL/raster_views.sql

@@ -11,32 +11,32 @@ CREATE VIEW raster_view_abs_time AS SELECT
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.creation_time, 
             A1.creator, 
-            A2.start_time, A2.end_time, A2.timezone,
+            A2.start_time, A2.end_time,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows,
             A4.nsres, A4.ewres, A4.min, A4.max,
-            A4.strds_register,
-            A4.number_of_cells
+            A4.number_of_cells, A5.registered_stds
             FROM raster_base A1, raster_absolute_time A2, 
-            raster_spatial_extent A3, raster_metadata A4 
-            WHERE A1.id = A2.id AND A1.id = A3.id AND A1.id = A4.id;
+            raster_spatial_extent A3, raster_metadata A4,
+            raster_stds_register A5
+            WHERE A1.id = A2.id AND A1.id = A3.id AND 
+            A1.id = A4.id AND A1.id = A5.id;
 
 CREATE VIEW raster_view_rel_time AS SELECT 
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.creation_time, 
             A1.creator, 
-            A2.start_time, A2.end_time,
+            A2.start_time, A2.end_time, A2.unit,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows,
             A4.nsres, A4.ewres, A4.min, A4.max,
-            A4.strds_register,
-            A4.number_of_cells
+            A4.number_of_cells, A5.registered_stds
             FROM raster_base A1, raster_relative_time A2, 
-            raster_spatial_extent A3, raster_metadata A4 
-            WHERE A1.id = A2.id AND A1.id = A3.id AND A1.id = A4.id;
+            raster_spatial_extent A3, raster_metadata A4,
+            raster_stds_register A5
+            WHERE A1.id = A2.id AND A1.id = A3.id AND 
+            A1.id = A4.id AND A1.id = A5.id;
+
+

+ 10 - 0
lib/temporal/SQL/sqlite3_delete_trigger.sql

@@ -1,5 +1,10 @@
+--#############################################################################
+-- This SQL script generates the sqlite3 trigger
+--
+-- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
+--#############################################################################
 
+-- Create trigger for automated deletion of dependent rows
 
 CREATE TRIGGER delete_strds_base AFTER DELETE ON strds_base
   BEGIN
@@ -15,6 +20,7 @@ CREATE TRIGGER delete_raster_base AFTER DELETE ON raster_base
     DELETE FROM raster_relative_time WHERE id = old.id;
     DELETE FROM raster_spatial_extent WHERE id = old.id;
     DELETE FROM raster_metadata WHERE id = old.id;
+    DELETE FROM raster_stds_register WHERE id = old.id;
   END;
 
 CREATE TRIGGER delete_str3ds_base AFTER DELETE ON str3ds_base
@@ -31,6 +37,7 @@ CREATE TRIGGER delete_raster3d_base AFTER DELETE ON raster3d_base
     DELETE FROM raster3d_relative_time WHERE id = old.id;
     DELETE FROM raster3d_spatial_extent WHERE id = old.id;
     DELETE FROM raster3d_metadata WHERE id = old.id;
+    DELETE FROM raster3d_stds_register WHERE id = old.id;
   END;
 
 CREATE TRIGGER delete_stvds_base AFTER DELETE ON stvds_base
@@ -47,5 +54,7 @@ CREATE TRIGGER delete_vector_base AFTER DELETE ON vector_base
     DELETE FROM vector_relative_time WHERE id = old.id;
     DELETE FROM vector_spatial_extent WHERE id = old.id;
     DELETE FROM vector_metadata WHERE id = old.id;
+    DELETE FROM vector_stds_register WHERE id = old.id;
   END;
 
+

+ 51 - 0
lib/temporal/SQL/sqlite3_indexes.sql

@@ -0,0 +1,51 @@
+--#############################################################################
+-- This SQL script generates the sqlite3 indexes
+--
+-- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
+--#############################################################################
+
+-- Indexes for space time datasets
+
+CREATE INDEX strds_base_index ON strds_base (id);
+CREATE INDEX strds_relative_time_index ON strds_relative_time (id);
+CREATE INDEX strds_absolute_time_index ON strds_absolute_time (id);
+CREATE INDEX strds_spatial_extent_index ON strds_spatial_extent (id);
+
+CREATE INDEX str3ds_base_index ON str3ds_base (id);
+CREATE INDEX str3ds_relative_time_index ON str3ds_relative_time (id);
+CREATE INDEX str3ds_absolute_time_index ON str3ds_absolute_time (id);
+CREATE INDEX str3ds_spatial_extent_index ON str3ds_spatial_extent (id);
+
+CREATE INDEX stvds_base_index ON stvds_base (id);
+CREATE INDEX stvds_relative_time_index ON stvds_relative_time (id);
+CREATE INDEX stvds_absolute_time_index ON stvds_absolute_time (id);
+CREATE INDEX stvds_spatial_extent_index ON stvds_spatial_extent (id);
+
+CREATE INDEX str3ds_metadata_index ON str3ds_metadata (id);
+CREATE INDEX strds_metadata_index ON strds_metadata (id);
+CREATE INDEX stvds_metadata_index ON stvds_metadata (id);
+
+-- Indexes for raster, vector and 3D raster maps
+
+CREATE INDEX raster_base_index ON raster_base (id);
+CREATE INDEX raster_relative_time_index ON raster_relative_time (id, start_time, end_time);
+CREATE INDEX raster_absolute_time_index ON raster_absolute_time (id, start_time, end_time);
+CREATE INDEX raster_spatial_extent_index ON raster_spatial_extent (id);
+CREATE INDEX raster_stds_register_index ON raster_stds_register (id);
+
+
+CREATE INDEX raster3d_base_index ON raster3d_base (id);
+CREATE INDEX raster3d_relative_time_index ON raster3d_relative_time (id, start_time, end_time);
+CREATE INDEX raster3d_absolute_time_index ON raster3d_absolute_time (id, start_time, end_time);
+CREATE INDEX raster3d_spatial_extent_index ON raster3d_spatial_extent (id);
+CREATE INDEX raster3d_stds_register_index ON raster3d_stds_register (id);
+
+CREATE INDEX vector_base_index ON vector_base (id);
+CREATE INDEX vector_relative_time_index ON vector_relative_time (id, start_time, end_time);
+CREATE INDEX vector_absolute_time_index ON vector_absolute_time (id, start_time, end_time);
+CREATE INDEX vector_spatial_extent_index ON vector_spatial_extent (id);
+CREATE INDEX vector_stds_register_index ON vector_stds_register (id);
+
+CREATE INDEX raster3d_metadata_index ON raster3d_metadata (id);
+CREATE INDEX raster_metadata_index ON raster_metadata (id);
+CREATE INDEX vector_metadata_index ON vector_metadata (id);

+ 2 - 6
lib/temporal/SQL/stds_map_register_table_template.sql

@@ -8,16 +8,10 @@
 --#############################################################################
 
 -- SPACETIME_NAME is a placeholder for specific stds name (SQL compliant): name_mapset
 -- GRASS_MAP is a placeholder for specific map type: raster, raster3d or vector
-
---PRAGMA foreign_keys = ON;
 
 -- This table stores the.ids of the GRASS_MAP maps registered in the current spacetime GRASS_MAP table
 CREATE TABLE  SPACETIME_NAME_GRASS_MAP_register (
-  id VARCHAR NOT NULL, -- This colum is a primary foreign key storing the registered GRASS_MAP map.ids
-  FOREIGN KEY (id) REFERENCES  GRASS_MAP_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  id VARCHAR NOT NULL, -- This colum is a primary key storing the registered GRASS_MAP map.ids
+  PRIMARY KEY (id)
 );
-
-CREATE INDEX SPACETIME_NAME_GRASS_MAP_register_index ON SPACETIME_NAME_GRASS_MAP_register (id);

+ 0 - 39
lib/temporal/SQL/stds_raster3d_register_trigger_template.sql

@@ -1,113 +0,0 @@
---#############################################################################
---
---#############################################################################
-
-
---PRAGMA foreign_keys = ON;
-
---CREATE TRIGGER SPACETIME_NAME_raster3d_metadata_register_insert_trigger AFTER INSERT ON SPACETIME_NAME_raster3d_register 
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---
---CREATE TRIGGER SPACETIME_NAME_raster3d_metadata_register_delete_trigger AFTER DELETE ON SPACETIME_NAME_raster3d_register 
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---			(SELECT id FROM SPACETIME_NAME_raster3d_register)
---
---
---
---
---
---
---
---
---

+ 0 - 51
lib/temporal/SQL/stds_raster_register_trigger_template.sql

@@ -1,113 +0,0 @@
---#############################################################################
---
---#############################################################################
-
-
---PRAGMA foreign_keys = ON;
-
---CREATE TRIGGER SPACETIME_NAME_raster_metadata_register_insert_trigger AFTER INSERT ON SPACETIME_NAME_raster_register 
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---
---CREATE TRIGGER SPACETIME_NAME_raster_metadata_register_delete_trigger AFTER DELETE ON SPACETIME_NAME_raster_register 
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---			(SELECT id FROM SPACETIME_NAME_raster_register)
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---
---

+ 10 - 9
lib/temporal/SQL/stds_tables_template.sql

@@ -7,8 +7,6 @@
 
 -- STDS is a placeholder for specific space-time dataset type: strds, str3ds, stvds
 
---PRAGMA foreign_keys = ON;
-
 CREATE TABLE  STDS_base (
   id VARCHAR NOT NULL,                 -- Id of the space-time dataset, name@mapset this is the primary key
   name VARCHAR NOT NULL,               -- name of the space-time dataset
@@ -17,34 +15,31 @@ CREATE TABLE  STDS_base (
   temporal_type VARCHAR NOT NULL,      -- The temporal type of the dataset "absolute" or "relative" 
   semantic_type VARCHAR NOT NULL,      -- The semantic data description used for aggregation/decomposition algorithm selection: min, max, mean or sum
   creation_time TIMESTAMP NOT NULL,    -- The time of creation of the space-time dataset
+  modification_time TIMESTAMP NOT NULL,-- The time of the last modification of the space time dataset
   PRIMARY KEY (id)
 );
 
 CREATE TABLE  STDS_relative_time (
-  id VARCHAR NOT NULL,            -- Id of the space-time dataset, this is the primary foreign key
+  id VARCHAR NOT NULL,            -- Id of the space-time dataset, this is the primary key
   start_time INTEGER,             -- The relative valid start time 
   end_time INTEGER,               -- The relative valid end time 
   granularity INTEGER,            -- The granularity 
   unit VARCHAR,                   -- The relative time unit, available are "years, months, days, minutes, seconds"
   map_time VARCHAR,               -- The temporal type of the registered maps, may be interval, point or mixed
-  FOREIGN KEY (id) REFERENCES  STDS_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
 CREATE TABLE  STDS_absolute_time (
-  id VARCHAR NOT NULL,            -- Id of the space-time dataset, this is the primary foreign key
+  id VARCHAR NOT NULL,            -- Id of the space-time dataset, this is the primary key
   start_time TIMESTAMP,           -- Start of the valid time, can be NULL if no map is registered
   end_time TIMESTAMP,             -- End of the valid time, can be NULL if no map is registered
   granularity VARCHAR,            -- The granularity "NNN seconds, NNN minutes, NNN hours, NNN days, NNN months, NNN years"
-  timezone VARCHAR,      -- The timezone of the valid time stored as string. This is currently not in use. Instead the timezone is set in the datetime strings 
   map_time VARCHAR,               -- The temporal type of the registered maps, may be interval, point or mixed
-  FOREIGN KEY (id) REFERENCES  STDS_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
 CREATE TABLE  STDS_spatial_extent (
-  id VARCHAR NOT NULL,      -- Id of the space-time dataset, this is the primary foreign key
+  id VARCHAR NOT NULL,      -- Id of the space-time dataset, this is the primary key
   north DOUBLE PRECISION,   -- The spatial north extent, derived from the registered maps
   south DOUBLE PRECISION,   -- The spatial south extent, derived from the registered maps
   east DOUBLE PRECISION,    -- The spatial east extent, derived from the registered maps
@@ -52,5 +47,8 @@ CREATE TABLE  STDS_spatial_extent (
   top DOUBLE PRECISION,     -- The spatial top extent, derived from the registered maps
   bottom DOUBLE PRECISION,  -- The spatial bottom extent, derived from the registered maps
   proj VARCHAR,      -- The projection of the space time dataset (XY of LL)
-  FOREIGN KEY (id) REFERENCES  STDS_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
+
+
+

+ 0 - 3
lib/temporal/SQL/stds_vector_register_trigger_template.sql

@@ -1,6 +0,0 @@
---#############################################################################
---
---#############################################################################

+ 5 - 4
lib/temporal/SQL/str3ds_metadata_table.sql

@@ -4,10 +4,8 @@
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 
---PRAGMA foreign_keys = ON;
-
 CREATE TABLE  str3ds_metadata (
-  id VARCHAR NOT NULL,          -- Id of the space time 3D raster dataset, this is the primary foreign key
+  id VARCHAR NOT NULL,          -- Id of the space time 3D raster dataset, this is the primary key
   raster3d_register VARCHAR,    -- The id of the table in which the 3D raster maps are registered for this dataset
   number_of_maps INTEGER,       -- The number of registered 3D raster maps
   max_min DOUBLE PRECISION,     -- The minimal maximum of the registered 3D raster maps
@@ -20,8 +18,11 @@ CREATE TABLE  str3ds_metadata (
   ewres_max DOUBLE PRECISION,   -- The highest east-west resolution of the registered 3D raster maps
   tbres_min DOUBLE PRECISION,   -- The lowest top-bottom resolution of the registered 3D raster maps
   tbres_max DOUBLE PRECISION,   -- The highest top-bottom resolution of the registered 3D raster maps
+  aggregation_type VARCHAR,     -- The aggregation type of the dataset (mean, min, max, ...) set by aggregation modules
   title VARCHAR,                -- Title of the space time 3D raster dataset
   description VARCHAR,          -- Detailed description of the space time 3D raster dataset
   command VARCHAR,              -- The command that was used to create the space time 3D raster dataset
-  FOREIGN KEY (id) REFERENCES  str3ds_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
+
+

+ 7 - 6
lib/temporal/SQL/str3ds_views.sql

@@ -9,11 +9,9 @@
 CREATE VIEW str3ds_view_abs_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
-            A2.start_time, A2.end_time, A2.timezone, A2.granularity,
+            A2.start_time, A2.end_time, A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster3d_register,
             A4.number_of_maps, 
@@ -21,7 +19,7 @@ CREATE VIEW str3ds_view_abs_time AS SELECT
             A4.nsres_max, A4.ewres_max, 
             A4.tbres_min, A4.tbres_max, 
             A4.min_min, A4.min_max,
-            A4.max_min, A4.max_max,
+            A4.max_min, A4.max_max, A4.aggregation_type,
             A4.title, A4.description, A4.command
             FROM str3ds_base A1, str3ds_absolute_time A2,  
             str3ds_spatial_extent A3, str3ds_metadata A4 WHERE A1.id = A2.id AND 
@@ -30,11 +28,9 @@ CREATE VIEW str3ds_view_abs_time AS SELECT
 CREATE VIEW str3ds_view_rel_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
-            A2.start_time, A2.end_time, A2.granularity,
+            A2.start_time, A2.end_time, A2.unit, A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster3d_register,
             A4.number_of_maps, 
@@ -42,8 +38,9 @@ CREATE VIEW str3ds_view_rel_time AS SELECT
             A4.nsres_max, A4.ewres_max, 
             A4.tbres_min, A4.tbres_max, 
             A4.min_min, A4.min_max,
-            A4.max_min, A4.max_max,
+            A4.max_min, A4.max_max, A4.aggregation_type,
             A4.title, A4.description, A4.command
             FROM str3ds_base A1, str3ds_relative_time A2,  
             str3ds_spatial_extent A3, str3ds_metadata A4 WHERE A1.id = A2.id AND 
             A1.id = A3.id AND A1.id = A4.id;
+

+ 5 - 3
lib/temporal/SQL/strds_metadata_table.sql

@@ -4,10 +4,9 @@
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 
---PRAGMA foreign_keys = ON;
 
 CREATE TABLE  strds_metadata (
-  id VARCHAR NOT NULL,          -- Id of the space-time dataset, this is the primary foreign key
+  id VARCHAR NOT NULL,          -- Id of the space-time dataset, this is the primary key
   raster_register VARCHAR,      -- The id of the table in which the raster maps are registered for this dataset
   number_of_maps INTEGER,       -- The number of registered raster maps
   max_min DOUBLE PRECISION,     -- The minimal maximum of the registered raster maps
@@ -18,8 +17,11 @@ CREATE TABLE  strds_metadata (
   nsres_max DOUBLE PRECISION,   -- The highest north-south resolution of the registered raster maps
   ewres_min DOUBLE PRECISION,   -- The lowest east-west resolution of the registered raster maps
   ewres_max DOUBLE PRECISION,   -- The highest east-west resolution of the registered raster maps
+  aggregation_type VARCHAR,     -- The aggregation type of the dataset (mean, min, max, ...) set by aggregation modules
   title VARCHAR,                -- Title of the space-time raster dataset
   description VARCHAR,          -- Detailed description of the space-time raster dataset
   command VARCHAR,              -- The command that was used to create the space time raster dataset
-  FOREIGN KEY (id) REFERENCES  strds_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
+
+

+ 7 - 6
lib/temporal/SQL/strds_views.sql

@@ -9,18 +9,16 @@
 CREATE VIEW strds_view_abs_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
-            A2.start_time, A2.end_time, A2.timezone, A2.granularity,
+            A2.start_time, A2.end_time, A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster_register,
             A4.number_of_maps, 
             A4.nsres_min, A4.ewres_min, 
             A4.nsres_max, A4.ewres_max, 
             A4.min_min, A4.min_max,
-            A4.max_min, A4.max_max,
+            A4.max_min, A4.max_max, A4.aggregation_type,
             A4.title, A4.description, A4.command	
             FROM strds_base A1, strds_absolute_time A2,  
             strds_spatial_extent A3, strds_metadata A4 WHERE A1.id = A2.id AND 
@@ -29,19 +27,18 @@ CREATE VIEW strds_view_abs_time AS SELECT
 CREATE VIEW strds_view_rel_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
-            A2.start_time, A2.end_time, A2.granularity,
+            A2.start_time, A2.end_time, A2.unit, A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster_register,
             A4.number_of_maps, 
             A4.nsres_min, A4.ewres_min, 
             A4.nsres_max, A4.ewres_max, 
             A4.min_min, A4.min_max,
-            A4.max_min, A4.max_max,
+            A4.max_min, A4.max_max, A4.aggregation_type,
             A4.title, A4.description, A4.command
             FROM strds_base A1, strds_relative_time A2,  
             strds_spatial_extent A3, strds_metadata A4 WHERE A1.id = A2.id AND 
             A1.id = A3.id AND A1.id = A4.id;
+

+ 4 - 5
lib/temporal/SQL/stvds_metadata_table.sql

@@ -4,10 +4,8 @@
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 
---PRAGMA foreign_keys = ON;
-
 CREATE TABLE  stvds_metadata (
-  id VARCHAR NOT NULL,    -- Name of the space-time vector dataset, this is the primary foreign key
+  id VARCHAR NOT NULL,    -- Name of the space-time vector dataset, this is the primary key
   vector_register VARCHAR,-- The id of the table in which the vector maps are registered for this dataset
   number_of_maps INTEGER, -- The number of registered vector maps
   title VARCHAR,          -- Title of the space-time vector dataset
@@ -24,6 +22,7 @@ CREATE TABLE  stvds_metadata (
   areas INTEGER,          -- The number of areas accumulated from all registered maps (topological information)
   islands INTEGER,        -- The number of islands accumulated from all registered maps (topological information)
   holes INTEGER,          -- The number of holes accumulated from all registered maps (topological information)
-  volumes INTEGER,        -- The number of volumes accumulated from all registered maps (topological information)
-  FOREIGN KEY (id) REFERENCES  stvds_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  volumes INTEGER,        -- The number of volumes accumulated from all registered maps (topological information)           -- The command that was used to create the space time raster dataset
+  PRIMARY KEY (id)
 );
+

+ 9 - 7
lib/temporal/SQL/stvds_views.sql

@@ -9,11 +9,9 @@
 CREATE VIEW stvds_view_abs_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
-            A2.start_time, A2.end_time, A2.timezone,
+            A2.start_time, A2.end_time,
             A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.vector_register,
@@ -28,15 +26,13 @@ CREATE VIEW stvds_view_abs_time AS SELECT
 
 CREATE VIEW stvds_view_rel_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
-            A1.semantic_type, 
-            A1.creation_time, 
-            A1.creator, 
-            A2.start_time, A2.end_time, A2.granularity,
+            A1.semantic_type,
+            A1.creation_time, A1.modification_time,
+            A1.creator,
+            A2.start_time, A2.end_time, A2.unit, A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.vector_register,
-            A4.number_of_maps, 
+            A4.number_of_maps,
             A4.title, A4.description, A4.command, A4.points, A4.lines,
             A4.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.primitives, A4.nodes, A4.areas, A4.islands,
@@ -44,3 +40,5 @@ CREATE VIEW stvds_view_rel_time AS SELECT
             FROM stvds_base A1, stvds_relative_time A2,  
             stvds_spatial_extent A3, stvds_metadata A4 WHERE A1.id = A2.id AND 
             A1.id = A3.id AND A1.id = A4.id;
+
+

+ 0 - 836
lib/temporal/SQL/test.temporal.py

@@ -1,836 +0,0 @@
-import os
-from grass.script.tgis_core import *
-from grass.script.tgis_base import *
-from grass.script.tgis_temporal_extent import *
-from grass.script.tgis_spatial_extent import *
-from grass.script.tgis_metadata import *
-from grass.script.tgis_abstract_datasets import *
-from grass.script.tgis_space_time_datasets import *
-import grass.script as grass
-###############################################################################
-
-def test_dict_sql_serializer():
-    t = dict_sql_serializer()
-    t.test()
-
-def test_dataset_identifer():
-	for i in range(2):
-	    base = raster_base(ident="soil" + str(i) + "@PERMANENT", name="soil" + str(i), mapset="PERMANENT", creator="soeren", temporal_type="absolute", revision=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_creator("rene")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = raster3d_base(ident="soil" + str(i) + "@PERMANENT", name="soil" + str(i), mapset="PERMANENT", temporal_type="absolute", creator="soeren")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_creator("rene")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = vector_base(ident="soil" + str(i) + "@PERMANENT", name="soil" + str(i), mapset="PERMANENT", temporal_type="absolute", creator="soeren")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_creator("rene")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = strds_base(ident="soil" + str(i) + "@PERMANENT", name="soil" + str(i), mapset="PERMANENT", creator="soeren", semantic_type="event", temporal_type="absolute", revision=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_creator("rene")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = str3ds_base(ident="soil" + str(i) + "@PERMANENT", name="soil" + str(i), mapset="PERMANENT", temporal_type="absolute", semantic_type="event", creator="soeren")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_creator("rene")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = stvds_base(ident="soil" + str(i) + "@PERMANENT", name="soil" + str(i), mapset="PERMANENT", temporal_type="absolute", semantic_type="event", creator="soeren")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_creator("rene")
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-def test_absolute_timestamp():
-	for i in range(2):
-	    base = raster_absolute_time(ident="soil" + str(i) + "@PERMANENT", start_time=datetime(2011,01,01), end_time=datetime(2011,07,01), timezone=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_start_time(datetime(2010,01,01))
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = raster3d_absolute_time(ident="soil" + str(i) + "@PERMANENT", start_time=datetime(2011,01,01), end_time=datetime(2011,07,01), timezone=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_start_time(datetime(2010,01,01))
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = vector_absolute_time(ident="soil" + str(i) + "@PERMANENT", start_time=datetime(2011,01,01), end_time=datetime(2011,07,01), timezone=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_start_time(datetime(2010,01,01))
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-	for i in range(2):
-	    base = strds_absolute_time(ident="soil" + str(i) + "@PERMANENT", start_time=datetime(2011,01,01), end_time=datetime(2011,07,01), granularity="1 day", timezone=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_start_time(datetime(2010,01,01))
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = str3ds_absolute_time(ident="soil" + str(i) + "@PERMANENT", start_time=datetime(2011,01,01), end_time=datetime(2011,07,01), granularity="1 day", timezone=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_start_time(datetime(2010,01,01))
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = stvds_absolute_time(ident="soil" + str(i) + "@PERMANENT", start_time=datetime(2011,01,01), end_time=datetime(2011,07,01), granularity="1 day", timezone=1)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_start_time(datetime(2010,01,01))
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-def test_spatial_extent():
-	for i in range(2):
-	    base = raster_spatial_extent(ident="soil" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_north(120+i)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = raster3d_spatial_extent(ident="soil" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_north(120+i)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = vector_spatial_extent(ident="soil" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_north(120+i)
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-	for i in range(2):
-	    base = strds_spatial_extent(ident="soil" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_north(120+i)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = str3ds_spatial_extent(ident="soil" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_north(120+i)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = stvds_spatial_extent(ident="soil" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_north(120+i)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-def test_relative_timestamp():
-	for i in range(2):
-	    base = raster_relative_time(ident="soil" + str(i) + "@PERMANENT", interval=i)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_interval(i+1)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = raster3d_relative_time(ident="soil" + str(i) + "@PERMANENT", interval=i)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_interval(i+1)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = vector_relative_time(ident="soil" + str(i) + "@PERMANENT", interval=i)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_interval(i+1)
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-	for i in range(2):
-	    base = strds_relative_time(ident="soil" + str(i) + "@PERMANENT", interval=i, granularity=5.5)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_interval(i+1)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = str3ds_relative_time(ident="soil" + str(i) + "@PERMANENT", interval=i, granularity=5.5)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_interval(i+1)
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = stvds_relative_time(ident="soil" + str(i) + "@PERMANENT", interval=i, granularity=5.5)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_interval(i+1)
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-def test_map_metadata():
-	for i in range(2):
-	    base = raster_metadata(ident="soil" + str(i) + "@PERMANENT", strds_register="PERMANENT_soil_strds_register", datatype="CELL", \
-			    cols=500, rows=400, number_of_cells=200000,nsres=1, ewres=1, min=0, max=33)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_datatype("FCELL")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = raster3d_metadata(ident="soil" + str(i) + "@PERMANENT", str3ds_register="PERMANENT_soil_str3ds_register", datatype="FCELL", \
-			    cols=500, rows=400, depths=20, number_of_cells=200000,nsres=1, ewres=1, tbres=10, min=0, max=33)
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_datatype("DCELL")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = vector_metadata(ident="soil" + str(i) + "@PERMANENT", stvds_register="PERMANENT_soil_stvds_register")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_stvds_register("PERMANENT_soil_stvds_register")
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-	for i in range(2):
-	    base = strds_metadata(ident="soil" + str(i) + "@PERMANENT", raster_register="PERMANENT_soil_raster_register", \
-                                   title="Test", description="Test description")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_title("More tests")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = str3ds_metadata(ident="soil" + str(i) + "@PERMANENT", raster3d_register="PERMANENT_soil_raster3d_register", \
-                                   title="Test", description="Test description")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_title("More tests")
-	    base.update()
-	    base.select()
-	    base.print_self()
-
-	for i in range(2):
-	    base = stvds_metadata(ident="soil" + str(i) + "@PERMANENT", vector_register="PERMANENT_soil_vector_register", \
-                                   title="Test", description="Test description")
-	    base.insert()
-	    base.select()
-	    base.print_self()
-	    base.clear()
-	    base.set_title("More tests")
-	    base.update()
-	    base.select()
-	    base.print_self()
-            
-def test_base_absolute_time_extent_metadata():
-
-	for i in range(10):
-	    base = vector_base(ident="water" + str(i) + "@PERMANENT", name="water" + str(i), mapset="PERMANENT", creator="soeren")
-	    base.insert()
-	    base = raster_base(ident="water" + str(i) + "@PERMANENT", name="water" + str(i), mapset="PERMANENT", creator="soeren")
-	    base.insert()
-	    base = raster3d_base(ident="water" + str(i) + "@PERMANENT", name="water" + str(i), mapset="PERMANENT", creator="soeren")
-	    base.insert()
-
-	for i in range(10):
-	    base = vector_base(ident="water" + str(i) + "@PERMANENT")
-	    base.set_creator("rene")
-	    base.update()
-	    base = raster_base(ident="water" + str(i) + "@PERMANENT")
-	    base.set_creator("rene")
-	    base.update()
-	    base = raster3d_base(ident="water" + str(i) + "@PERMANENT")
-	    base.set_creator("rene")
-	    base.update()
-
-	for i in range(10):
-	    base = vector_absolute_time(ident="water" + str(i) + "@PERMANENT", start_time=datetime.now(), end_time=datetime.now(), timezone=1)
-	    base.insert()
-	    base = raster_absolute_time(ident="water" + str(i) + "@PERMANENT", start_time=datetime.now(), end_time=datetime.now(), timezone=1)
-	    base.insert()
-	    base = raster3d_absolute_time(ident="water" + str(i) + "@PERMANENT", start_time=datetime.now(), end_time=datetime.now(), timezone=1)
-	    base.insert()
-
-	for i in range(10):
-	    base = vector_absolute_time(ident="water" + str(i) + "@PERMANENT")
-	    base.set_start_time(datetime(2010, 6, 1))
-	    base.update()
-	    base = raster_absolute_time(ident="water" + str(i) + "@PERMANENT")
-	    base.set_start_time(datetime(2010, 6, 1))
-	    base.update()
-	    base = raster3d_absolute_time(ident="water" + str(i) + "@PERMANENT")
-	    base.set_start_time(datetime(2010, 6, 1))
-	    base.update()
-
-	for i in range(10):
-	    base = vector_spatial_extent(ident="water" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-	    base = raster_spatial_extent(ident="water" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=0, bottom=0)
-	    base.insert()
-	    base = raster3d_spatial_extent(ident="water" + str(i) + "@PERMANENT", north=100 + i, south=10+i, east=50+i, west=20+i, top=i, bottom=0)
-	    base.insert()
-
-	for i in range(10):
-	    base = vector_spatial_extent(ident="water" + str(i) + "@PERMANENT")
-	    base.set_north(120 + i)
-	    base.set_south(20 + i)
-	    base.update()
-	    base = raster_spatial_extent(ident="water" + str(i) + "@PERMANENT")
-	    base.set_north(120 + i)
-	    base.set_south(20 + i)
-	    base.update()
-	    base = raster3d_spatial_extent(ident="water" + str(i) + "@PERMANENT")
-	    base.set_north(120 + i)
-	    base.set_south(20 + i)
-	    base.update()
-
-	for i in range(10):
-	    base = vector_metadata(ident="water" + str(i) + "@PERMANENT", stvds_register="PERMANENT_water_stvds_register")
-	    base.insert()
-	    base = raster_metadata(ident="water" + str(i) + "@PERMANENT", strds_register="PERMANENT_water_strds-register", datatype="CELL", \
-			    cols=500, rows=400, number_of_cells=200000,nsres=1, ewres=1, min=0, max=33)
-	    base.insert()
-	    base = raster3d_metadata(ident="water" + str(i) + "@PERMANENT", str3ds_register="PERMANENT_water_str3ds-register", datatype="FCELL", \
-			    cols=500, rows=400, depths=20, number_of_cells=200000,nsres=1, ewres=1, tbres=10, min=0, max=33)
-	    base.insert()
-
-	for i in range(10):
-	    base = vector_metadata(ident="water" + str(i) + "@PERMANENT")
-	    base.set_stvds_register("PERMANENT_water_stvds_register")
-	    base.update()
-	    base = raster_metadata(ident="water" + str(i) + "@PERMANENT")
-	    base.set_datatype("DCELL")
-	    base.update()
-	    base = raster3d_metadata(ident="water" + str(i) + "@PERMANENT")
-	    base.set_datatype("DCELL")
-	    base.update()
-
-	for i in range(10):
-	    base = vector_base(ident="water" + str(i) + "@PERMANENT")
-	    base.select()
-	    base.print_self()
-	    base = raster_base(ident="water" + str(i) + "@PERMANENT")
-	    base.select()
-	    base.print_self()
-	    base = raster3d_base(ident="water" + str(i) + "@PERMANENT")
-	    base.select()
-	    base.print_self()
-
-	print "Create a raster object"
-
-	rds = raster_dataset("water0@PERMANENT")
-	rds.select()
-	rds.print_self()
-
-	print rds.temporal_relation(rds)
-
-def test_absolut_time_temporal_relations():
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2001, month=1, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2001, month=1, day=1), \
-		                                        end_time=datetime(year=2002, month=1, day=1), timezone=1)
-
-    print "Precedes: ", A.temporal_relation(B)
-    print "Follows:  ", B.temporal_relation(A)
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2001, month=1, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2001, month=1, day=2), \
-		                                        end_time=datetime(year=2002, month=1, day=1), timezone=1)
-
-    print "Before:   ", A.temporal_relation(B)
-    print "After:    ", B.temporal_relation(A)
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2001, month=1, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2002, month=1, day=1), timezone=1)
-
-    print "Starts:   ", A.temporal_relation(B)
-    print "Started:  ", B.temporal_relation(A)
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2001, month=1, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2000, month=1, day=2), \
-		                                        end_time=datetime(year=2001, month=1, day=1), timezone=1)
-
-    print "Finished: ", A.temporal_relation(B)
-    print "Finishes: ", B.temporal_relation(A)
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2001, month=1, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2000, month=3, day=1), \
-		                                        end_time=datetime(year=2000, month=9, day=1), timezone=1)
-
-    print "Contains: ", A.temporal_relation(B)
-    print "During:   ", B.temporal_relation(A)
-
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2000, month=6, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2000, month=3, day=1), \
-		                                        end_time=datetime(year=2000, month=9, day=1), timezone=1)
-
-    print "Overlap:   ", A.temporal_relation(B)
-    print "Overlapped:", B.temporal_relation(A)
-
-    A = raster_absolute_time(ident="test1@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2000, month=1, day=1), timezone=1)
-    B = raster_absolute_time(ident="test2@PERMANENT", start_time=datetime(year=2000, month=1, day=1), \
-		                                        end_time=datetime(year=2000, month=1, day=1), timezone=1)
-
-    print "Equivalent:", A.temporal_relation(B)
-    print "Equivalent:", B.temporal_relation(A)
-
-
-def test_raster_dataset():
-    
-    # Create a test map
-    grass.raster.mapcalc("test = sin(x()) + cos(y())", overwrite = True)
-    
-    name = "test"
-    mapset =  grass.gisenv()["MAPSET"]
-    
-    print "Create a raster object"
-
-    # We need to specify the name and the mapset as identifier
-    rds = raster_dataset(name + "@" + mapset)
-    
-    # Load data from the raster map in the mapset
-    rds.load()
-    
-    print "Is in db: ", rds.is_in_db()
-    
-    if rds.is_in_db():      
-        # Remove the entry if it is in the db
-        rds.delete()
-    
-    # Set the absolute valid time
-    rds.set_absolute_time(start_time= datetime(year=2000, month=1, day=1), \
-                            end_time= datetime(year=2010, month=1, day=1))
-    # Insert the map data into the SQL database
-    rds.insert()
-    # Print self info
-    rds.print_self()
-    # The temporal relation must be equal
-    print rds.temporal_relation(rds)
-
-def test_raster3d_dataset():
-    
-    # Create a test map
-    grass.raster3d.mapcalc3d("test = sin(x()) + cos(y()) + sin(z())", overwrite = True)
-    
-    name = "test"
-    mapset =  grass.gisenv()["MAPSET"]
-    
-    print "Create a raster object"
-
-    # We need to specify the name and the mapset as identifier
-    r3ds = raster3d_dataset(name + "@" + mapset)
-    
-    # Load data from the raster map in the mapset
-    r3ds.load()
-    
-    print "Is in db: ", r3ds.is_in_db()
-    
-    if r3ds.is_in_db():      
-        # Remove the entry if it is in the db
-        r3ds.delete()
-    
-    # Set the absolute valid time
-    r3ds.set_absolute_time(start_time= datetime(year=2000, month=1, day=1), \
-                            end_time= datetime(year=2010, month=1, day=1))
-                            
-    # Insert the map data into the SQL database
-    r3ds.insert()
-    # Print self info
-    r3ds.print_self()
-    # The temporal relation must be equal
-    print r3ds.temporal_relation(r3ds)
-
-def test_vector_dataset():
-    
-    # Create a test map
-    grass.run_command("v.random", output="test", n=20, column="height", zmin=0, \
-                      zmax=100, flags="z", overwrite = True)
-    
-    name = "test"
-    mapset =  grass.gisenv()["MAPSET"]
-    
-    print "Create a vector object"
-
-    # We need to specify the name and the mapset as identifier
-    vds = vector_dataset(name + "@" + mapset)
-    
-    # Load data from the raster map in the mapset
-    vds.load()
-    
-    print "Is in db: ", vds.is_in_db()
-    
-    if vds.is_in_db():      
-        # Remove the entry if it is in the db
-        vds.delete()
-    
-    # Set the absolute valid time
-    vds.set_absolute_time(start_time= datetime(year=2000, month=1, day=1), \
-                            end_time= datetime(year=2010, month=1, day=1))
-    # Insert the map data into the SQL database
-    vds.insert()
-    # Print self info
-    vds.print_self()
-    # The temporal relation must be equal
-    print vds.temporal_relation(vds)
-
-
-def test_strds_dataset():
-    
-    name = "strds_test_1"
-    mapset =  grass.gisenv()["MAPSET"]
-
-    print "Create a strds object"
-
-    # We need to specify the name and the mapset as identifier
-    strds = space_time_raster_dataset(ident = name + "@" + mapset)
-    # Check if in db
-    print "Is strds in db: ", strds.is_in_db()
-    # Create a new entry if not in db
-    if strds.is_in_db() == False:
-        strds.set_initial_values(temporal_type = "absolute", granularity="1 day",\
-        semantic_type="event", title="This is a test space time raster dataset", description="A space time raster dataset for testing")
-        strds.insert()
-    
-    # Reread the data from the db
-    strds.select()
-    # Print self info
-    #strds.print_self()
-
-    # Create a test maps
-    for i in range(11):
-        i = i + 1
-        grass.raster.mapcalc("test" + str(i) + " = sin(x()) + cos(y())", overwrite = True)
-    
-        name = "test" + str(i)
-        mapset =  grass.gisenv()["MAPSET"]
-        ident = name + "@" + mapset
-
-        print "Create a raster object"
-
-        # We need to specify the name and the mapset as identifier
-        rds = raster_dataset(ident)
-
-        # Load data from the raster map in the mapset
-        rds.load()
-
-        print "Is raster in db: ", rds.is_in_db()
-
-        if rds.is_in_db():      
-            rds.select()
-            rds.print_self()
-            # Remove the entry if it is in the db
-            rds.delete()
-            rds.reset(ident)
-            rds.load()
-
-        # Set the absolute valid time
-        rds.set_absolute_time(start_time= datetime(year=2000, month=i, day=1), \
-                                end_time= datetime(year=2000, month=i + 1, day=1))
-        # Insert the map data into the SQL database
-        rds.insert()
-        # Register the map in the space time raster dataset
-        strds.register_map(rds)
-        # Print self info
-        #rds.print_self()
-
-    strds.update_from_registered_maps()
-    strds.select()
-    # Print self info
-    strds.print_self()
-    # Delete the dataset
-    strds.delete()
-
-
-def test_str3ds_dataset():
-    
-    name = "str3ds_test_1"
-    mapset =  grass.gisenv()["MAPSET"]
-
-    print "Create a str3ds object"
-
-    # We need to specify the name and the mapset as identifier
-    str3ds = space_time_raster3d_dataset(ident = name + "@" + mapset)
-    # Check if in db
-    print "Is str3ds in db: ", str3ds.is_in_db()
-    # Create a new entry if not in db
-    if str3ds.is_in_db() == False:
-        str3ds.set_initial_values(temporal_type = "absolute", granularity="1 day",\
-        semantic_type="event", title="This is a test space time raster3d dataset", description="A space time raster3d dataset for testing")
-        str3ds.insert()
-    
-    # Reread the data from the db
-    str3ds.select()
-    # Print self info
-    #str3ds.print_self()
-
-    # Create a test maps
-    for i in range(11):
-        i = i + 1
-        grass.raster3d.mapcalc3d("test" + str(i) + " = sin(x()) + cos(y()) + z()", overwrite = True)
-    
-        name = "test" + str(i)
-        mapset =  grass.gisenv()["MAPSET"]
-        ident = name + "@" + mapset
-
-        print "Create a raster3d object"
-
-        # We need to specify the name and the mapset as identifier
-        r3ds = raster3d_dataset(ident)
-
-        # Load data from the raster3d map in the mapset
-        r3ds.load()
-
-        print "Is raster3d in db: ", r3ds.is_in_db()
-
-        if r3ds.is_in_db():      
-            r3ds.select()
-            r3ds.print_self()
-            # Remove the entry if it is in the db
-            r3ds.delete()
-            r3ds.reset(ident)
-            r3ds.load()
-
-        # Set the absolute valid time
-        r3ds.set_absolute_time(start_time= datetime(year=2000, month=i, day=1), \
-                                end_time= datetime(year=2000, month=i + 1, day=1))
-        # Insert the map data into the SQL database
-        r3ds.insert()
-        # Register the map in the space time raster3d dataset
-        str3ds.register_map(r3ds)
-        # Print self info
-        #r3ds.print_self()
-
-    str3ds.update_from_registered_maps()
-    str3ds.select()
-    # Print self info
-    str3ds.print_self()
-    # Delete the dataset
-    str3ds.delete()
-
-
-def test_stvds_dataset():
-
-    name = "stvds_test_1"
-    mapset =  grass.gisenv()["MAPSET"]
-
-    print "Create a stvds object"
-
-    # We need to specify the name and the mapset as identifier
-    stvds = space_time_vector_dataset(ident = name + "@" + mapset)
-    # Check if in db
-    print "Is stvds in db: ", stvds.is_in_db()
-    # Create a new entry if not in db
-    if stvds.is_in_db() == False:
-        stvds.set_initial_values(temporal_type = "absolute", granularity="1 day",\
-        semantic_type="event", title="This is a test space time vector dataset", description="A space time vector dataset for testing")
-        stvds.insert()
-
-    # Reread the data from the db
-    stvds.select()
-    # Print self info
-    #stvds.print_self()
-
-    # Create a test maps
-    for i in range(11):
-        name = "test" + str(i)
-        mapset =  grass.gisenv()["MAPSET"]
-        ident = name + "@" + mapset
-
-        i = i + 1
-        grass.run_command("v.random", output=name, n=100, zmin=0, zmax=100, column="height" ,flags="z" , overwrite = True)
-
-
-        print "Create a vector object"
-
-        # We need to specify the name and the mapset as identifier
-        vds = vector_dataset(ident)
-
-        # Load data from the raster map in the mapset
-        vds.load()
-
-        print "Is vector in db: ", vds.is_in_db()
-
-        if vds.is_in_db():
-            vds.select()
-            vds.print_self()
-            # Remove the entry if it is in the db
-            vds.delete()
-            vds.reset(ident)
-            vds.load()
-
-        # Set the absolute valid time
-        vds.set_absolute_time(start_time= datetime(year=2000, month=i, day=1), \
-                                end_time= datetime(year=2000, month=i + 1, day=1))
-        # Insert the map data into the SQL database
-        vds.insert()
-        # Register the map in the space time raster dataset
-        stvds.register_map(vds)
-        # Print self info
-        #vds.print_self()
-
-    stvds.update_from_registered_maps()
-    stvds.select()
-    # Print self info
-    stvds.print_self()
-    # Delete the dataset
-    stvds.delete()
-
-
-test_dict_sql_serializer()
-create_temporal_database()
-#test_dataset_identifer()
-#test_absolute_timestamp()
-#test_relative_timestamp()
-#test_spatial_extent()
-#test_map_metadata()
-#test_base_absolute_time_extent_metadata()
-#test_absolut_time_temporal_relations()
-
-test_raster_dataset()
-test_raster3d_dataset()
-test_vector_dataset()
-
-test_strds_dataset()
-test_str3ds_dataset()
-test_stvds_dataset()
-test_increment_datetime_by_string()

+ 1 - 0
lib/temporal/SQL/update_stds_spatial_temporal_extent_template.sql

@@ -64,3 +64,4 @@ UPDATE STDS_spatial_extent SET proj =
        (SELECT min(proj) FROM GRASS_MAP_spatial_extent WHERE GRASS_MAP_spatial_extent.id IN 
     		(SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
        ) WHERE id = 'SPACETIME_ID';
+

+ 1 - 0
lib/temporal/SQL/update_str3ds_metadata_template.sql

@@ -49,3 +49,4 @@ UPDATE str3ds_metadata SET tbres_max =
        (SELECT max(tbres) FROM raster3d_metadata WHERE raster3d_metadata.id IN 
     		(SELECT id FROM SPACETIME_NAME_raster3d_register)
        ) WHERE id = 'SPACETIME_ID';
+

+ 4 - 4
lib/temporal/SQL/vector_metadata_table.sql

@@ -10,8 +10,7 @@
 -- The metadata table 
 
 CREATE TABLE  vector_metadata (
-  id VARCHAR NOT NULL,    -- The id (PFK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary foreign key
-  stvds_register VARCHAR, -- The name of the table storing all space-time vector datasets in which this map is registered
+  id VARCHAR NOT NULL,    -- The id (PK) is the unique identifier for all tables, it is based on name and mapset (name@mapset) and is used as primary key
   is_3d BOOLEAN,          -- This is 1 if the vector map is 3d and 0 otherwise 
   points INTEGER,         -- The number of points
   lines INTEGER,          -- The number of lines
@@ -25,7 +24,8 @@ CREATE TABLE  vector_metadata (
   islands INTEGER,        -- The number of islands (topological information)
   holes INTEGER,          -- The number of holes (topological information)
   volumes INTEGER,        -- The number of volumes (topological information)
-  FOREIGN KEY (id) REFERENCES  vector_base (id) ON DELETE CASCADE ON UPDATE CASCADE
+  PRIMARY KEY (id)
 );
 
-CREATE INDEX vector_metadata_index ON vector_metadata (id);
+
+

+ 16 - 10
lib/temporal/SQL/vector_views.sql

@@ -11,27 +11,33 @@ CREATE VIEW vector_view_abs_time AS SELECT
             A1.name, A1.layer, A1.temporal_type,
             A1.creation_time, 
             A1.creator, 
-            A2.start_time, A2.end_time, A2.timezone,
+            A2.start_time, A2.end_time,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
-            A4.stvds_register, A4.is_3d, A4.points, A4.lines,
+            A4.is_3d, A4.points, A4.lines,
             A4.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.primitives, A4.nodes, A4.areas, A4.islands,
-            A4.holes, A4.volumes
+            A4.holes, A4.volumes, A5.registered_stds
             FROM vector_base A1, vector_absolute_time A2, 
-            vector_spatial_extent A3, vector_metadata A4 
-            WHERE A1.id = A2.id AND A1.id = A3.id AND A1.id = A4.id;
+            vector_spatial_extent A3, vector_metadata A4,
+            vector_stds_register A5
+            WHERE A1.id = A2.id AND A1.id = A3.id AND 
+            A1.id = A4.id AND A1.id = A5.id;
 
 CREATE VIEW vector_view_rel_time AS SELECT 
             A1.id, A1.mapset,
             A1.name, A1.layer, A1.temporal_type,
             A1.creation_time,
             A1.creator, 
-            A2.start_time, A2.end_time,
+            A2.start_time, A2.end_time, A2.unit,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
-            A4.stvds_register, A4.is_3d, A4.points, A4.lines,
+            A4.is_3d, A4.points, A4.lines,
             A4.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.primitives, A4.nodes, A4.areas, A4.islands,
-            A4.holes, A4.volumes
+            A4.holes, A4.volumes, A5.registered_stds
             FROM vector_base A1, vector_relative_time A2, 
-            vector_spatial_extent A3, vector_metadata A4 
-            WHERE A1.id = A2.id AND A1.id = A3.id AND A1.id = A4.id;
+            vector_spatial_extent A3, vector_metadata A4,
+            vector_stds_register A5
+            WHERE A1.id = A2.id AND A1.id = A3.id AND 
+            A1.id = A4.id AND A1.id = A5.id;
+
+

+ 33 - 10
temporal/benchmark.sh

@@ -11,7 +11,9 @@ export GRASS_OVERWRITE=1
 MAP_LIST="map_list.txt"
 rm ${MAP_LIST}
 
-NUM_MAPS=50000
+NUM_MAPS=10000
+
+echo "### Generate raster maps"
 
 count=1
 while [ $count -lt ${NUM_MAPS} ]; do
@@ -21,18 +23,39 @@ while [ $count -lt ${NUM_MAPS} ]; do
     count=$((count + 1))
 done
 
-t.create type=strds temporaltype=absolute output=benchmark1 title="Benchmark1" descr="Benchmark1 dataset"
-t.create type=strds temporaltype=absolute output=benchmark2 title="Benchmark2" descr="Benchmark2 dataset"
+echo "### Create space time datasets"
+
+time t.create type=strds temporaltype=absolute output=bench1 title="Bench1" descr="Bench1"
+time t.create type=strds temporaltype=absolute output=bench2 title="Bench2" descr="Bench2"
+time t.create type=strds temporaltype=absolute output=bench3 title="Bench3" descr="Bench3"
+time t.create type=strds temporaltype=absolute output=bench4 title="Bench4" descr="Bench4"
 
 echo "### Register maps"
-time t.register -i input=benchmark1  file=${MAP_LIST} start="2001-01-01 00:00:00" increment="1 hours"
-time t.register -i input=benchmark2  file=${MAP_LIST} start="2001-01-01 00:00:00" increment="1 hours"
+time t.register -i input=bench1  file=${MAP_LIST} start="2001-01-01 00:00:00" increment="1 day"
+echo "### Register maps again"
+time t.register input=bench2  file=${MAP_LIST}
+echo "### Register maps again"
+time t.register input=bench3  file=${MAP_LIST}
+echo "### Register maps again"
+time t.register input=bench4  file=${MAP_LIST}
 
 echo "### List maps"
-time t.rast.list input=benchmark1 column=name,start_time > "/dev/null"
-time t.rast.list input=benchmark2 column=name,start_time > "/dev/null"
+time t.rast.list input=bench1 column=name,start_time > "/dev/null"
+time t.rast.list input=bench2 column=name,start_time > "/dev/null"
+time t.rast.list input=bench3 column=name,start_time,end_time \
+    where="start_time > '2001-01-01'" > "/dev/null"
+time t.rast.list input=bench4 column=name,start_time,end_time,min,max \
+    where="start_time > '2001-01-01'" > "/dev/null"
+
+echo "### STRDS Infos"
+t.info bench1
+t.info bench2
+t.info bench3
+t.info bench4
 
 echo "### Remove STRDS and maps"
-time t.remove -rf type=strds input=benchmark1
-echo "### Remove STRDS"
-time t.remove type=strds input=benchmark2
+time t.remove type=strds input=bench1
+time t.remove type=strds input=bench2
+time t.remove type=strds input=bench3
+time t.remove -rf type=strds input=bench4
+

+ 3 - 1
temporal/t.rast.aggregate.ds/t.rast.aggregate.ds.py

@@ -124,7 +124,7 @@ def main():
             if new_map:
                 # Set the time stamp and write it to the raster map
                 if sp.is_time_absolute():
-                    new_map.set_absolute_time(start, end, None)
+                    new_map.set_absolute_time(start, end)
                 else:
                     new_map.set_relative_time(start,
                                               end, sp.get_relative_time_unit())
@@ -134,6 +134,8 @@ def main():
                 new_sp.register_map(new_map, dbif)
 
     # Update the spatio-temporal extent and the raster metadata table entries
+    new_sp.set_aggregation_type(method)
+    new_sp.metadata.update(dbif)
     new_sp.update_from_registered_maps(dbif)
 
     dbif.close()

+ 3 - 1
temporal/t.rast.aggregate/t.rast.aggregate.py

@@ -126,7 +126,7 @@ def main():
             if new_map:
                 # Set the time stamp and write it to the raster map
                 if sp.is_time_absolute():
-                    new_map.set_absolute_time(start, end, None)
+                    new_map.set_absolute_time(start, end)
                 else:
                     new_map.set_relative_time(start,
                                               end, sp.get_relative_time_unit())
@@ -138,6 +138,8 @@ def main():
                 count += 1
 
     # Update the spatio-temporal extent and the raster metadata table entries
+    new_sp.set_aggregation_type(method)
+    new_sp.metadata.update(dbif)
     new_sp.update_from_registered_maps(dbif)
 
     dbif.close()

+ 3 - 3
temporal/t.rast.gapfill/t.rast.gapfill.py

@@ -162,17 +162,17 @@ def main():
         id = _map.get_id()
         if overwrite_flags[id] == True:
             if _map.is_time_absolute():
-                start, end, tz = _map.get_absolute_time()
+                start, end = _map.get_absolute_time()
                 if _map.is_in_db():
                     _map.delete(dbif)
                 _map = sp.get_new_map_instance(id)
-                _map.set_absolute_time(start, end, tz)
+                _map.set_absolute_time(start, end)
             else:
                 start, end, unit = _map.get_relative_time()
                 if _map.is_in_db():
                     _map.delete(dbif)
                 _map = sp.get_new_map_instance(id)
-                _map.set_relative_time(start, end, tz)
+                _map.set_relative_time(start, end, unit)
         _map.load()
         _map.insert(dbif)
         sp.register_map(_map, dbif)

+ 1 - 1
temporal/t.rast.import/test.t.rast.import.relative.sh

@@ -35,7 +35,7 @@ t.create type=strds temporaltype=relative output=precip_rel \
     title="A test with input files" descr="A test with input files"
 
 # The first @test
-t.register -i type=rast input=precip_rel file="${n1}"  unit="years" 
+t.register type=rast input=precip_rel file="${n1}"  unit="years" 
 
 t.rast.export input=precip_rel output=strds_export.tar.bz2 compression=bzip2 format=GTiff workdir=test
 t.rast.export input=precip_rel output=strds_export.tar.gz compression=gzip format=GTiff workdir=test

+ 1 - 10
temporal/t.rast.series/t.rast.series.py

@@ -105,11 +105,6 @@ def main():
                                 method=method)
 
         if ret == 0 and not add_time:
-            if sp.is_time_absolute():
-                start_time, end_time, tz = sp.get_absolute_time()
-            else:
-                start_time, end_time, unit = sp.get_relative_time()
-
             # Create the time range for the output map
             if output.find("@") >= 0:
                 id = output
@@ -119,11 +114,7 @@ def main():
 
             map = sp.get_new_map_instance(id)
             map.load()
-
-            if sp.is_time_absolute():
-                map.set_absolute_time(start_time, end_time, tz)
-            else:
-                map.set_relative_time(start_time, end_time, unit)
+            map.set_temporal_extent(sp.get_temporal_extent())
 
             # Register the map in the temporal database
             if map.is_in_db():

+ 1 - 0
temporal/t.rast3d.univar/test.t.rast3d.univar.sh

@@ -15,6 +15,7 @@ r3.mapcalc --o expr="prec_6 = rand(0, 650)"
 
 t.create --o type=str3ds temporaltype=absolute output=precip_abs1 title="A test" descr="A test"
 t.register type=rast3d --v -i input=precip_abs1 maps=prec_1,prec_2,prec_3,prec_4,prec_5,prec_6 start="2001-01-15 12:05:45" increment="14 days"
+t.info type=str3ds input=precip_abs1
 
 # The first @test
 t.rast3d.univar -he input=precip_abs1 

+ 3 - 1
temporal/t.register/t.register.py

@@ -118,8 +118,10 @@ def main():
         type=type, name=name, maps=maps, file=file, start=start, end=end,
         unit=unit, increment=increment, dbif=None, interval=interval, fs=fs)
 
+
 ###############################################################################
 
 if __name__ == "__main__":
     options, flags = grass.parser()
-    main()
+ 
+    tgis.profile_function(main)

+ 60 - 0
temporal/t.register/test.t.register.raster.file.timezone.sh

@@ -0,0 +1,60 @@
+#!/bin/sh
+# This is a test to register and unregister raster maps in
+# space time raster input.
+# The raster maps will be registered in different space time raster
+# inputs
+# Maps have absolute time with time zone information
+
+# We need to set a specific region in the
+# @preprocess step of this test. We generate
+# raster with r.mapcalc and create two space time raster inputs
+# with absolute time
+# The region setting should work for UTM and LL test locations
+g.region s=0 n=80 w=0 e=120 b=0 t=50 res=10 res3=10 -p3
+
+r.mapcalc --o expr="prec_1 = rand(0, 550)"
+r.mapcalc --o expr="prec_2 = rand(0, 450)"
+r.mapcalc --o expr="prec_3 = rand(0, 320)"
+r.mapcalc --o expr="prec_4 = rand(0, 510)"
+r.mapcalc --o expr="prec_5 = rand(0, 300)"
+r.mapcalc --o expr="prec_6 = rand(0, 650)"
+
+n2=`g.tempfile pid=2 -d` # Map names and start time
+n3=`g.tempfile pid=3 -d` # Map names start time and increment
+
+cat > "${n2}" << EOF
+prec_1|2001-01-01 10:00:00 +02:00
+prec_2|2001-02-01 10:00:00 +02:00
+prec_3|2001-03-01 10:00:00 +02:00
+prec_4|2001-04-01 10:00:00 +02:00
+prec_5|2001-05-01 10:00:00 +02:00
+prec_6|2001-06-01 10:00:00 +02:00
+EOF
+cat "${n2}"
+
+cat > "${n3}" << EOF
+prec_1|2001-01-01 12:00:00 +01:00|2001-04-01 14:00:00 -01:00
+prec_2|2001-04-01 12:00:00 +01:00|2001-07-01 14:00:00 -01:00
+prec_3|2001-07-01 12:00:00 +01:00|2001-10-01 14:00:00 -01:00
+prec_4|2001-10-01 12:00:00 +01:00|2002-01-01 14:00:00 -01:00
+prec_5|2002-01-01 12:00:00 +01:00|2002-04-01 14:00:00 -01:00
+prec_6|2002-04-01 12:00:00 +01:00|2002-07-01 14:00:00 -01:00
+EOF
+cat "${n3}"
+
+# The first @test
+# We create the space time raster inputs and register the raster maps with absolute time interval
+t.create --o type=strds temporaltype=absolute output=precip_abs title="A test with input files" descr="A test with input files"
+
+# Test with input files
+# File 1
+# File 2
+t.register --o input=precip_abs file="${n2}"
+t.info type=strds input=precip_abs
+t.rast.list input=precip_abs
+# File 3
+t.register --o -i input=precip_abs file="${n3}"
+t.info type=strds input=precip_abs
+t.rast.list input=precip_abs
+
+t.remove -rf type=strds input=precip_abs

+ 3 - 1
temporal/t.remove/t.remove.py

@@ -53,6 +53,7 @@
 #% key: f
 #% description: Force recursive removing
 #%end
+
 import grass.script as grass
 import grass.temporal as tgis
 import grass.pygrass.modules as pyg
@@ -154,4 +155,5 @@ def main():
 
 if __name__ == "__main__":
     options, flags = grass.parser()
-    main()
+    
+    tgis.profile_function(main)

+ 4 - 4
temporal/t.support/t.support.py

@@ -130,10 +130,10 @@ def main():
             else:
                 # Delete the map from the temporal database
                 # We need to update all effected space time datasets
-                rows = map.get_registered_datasets(dbif)
-                if rows:
-                    for row in rows:
-                        dataset_dict[row["id"]] = row["id"]
+                datasets = map.get_registered_datasets(dbif)
+                if datasets:
+                    for dataset in datasets:
+                        dataset_dict[dataset] = dataset
                 # Collect the delete statements
                 statement += map.delete(dbif=dbif, update=False, execute=False)
 

+ 2 - 1
temporal/t.support/test.t.support.sh

@@ -28,6 +28,7 @@ t.info type=strds input=precip_rel1
 t.support --v type=strds input=precip_rel1 title="Test support" descr="This is the support test strds" semantictype=max
 t.info type=strds input=precip_rel1
 
+
 # Check metadata update
 t.info type=strds input=precip_abs1
 t.support --v type=strds input=precip_abs1 title="Test support" descr="This is the support test strds" semantictype=mean
@@ -59,4 +60,4 @@ t.info type=strds input=precip_abs2
 
 t.remove --v type=strds input=precip_abs1,precip_rel1
 t.unregister type=rast maps=prec_1,prec_2,prec_3,prec_4,prec_5,prec_6
-g.remove rast=prec_4,prec_5,prec_6
+g.remove rast=prec_4,prec_5,prec_6

+ 3 - 2
temporal/t.unregister/t.unregister.py

@@ -135,7 +135,7 @@ def main():
                 # Store all unique dataset ids in a dictionary
                 if datasets:
                     for dataset in datasets:
-                        update_dict[dataset["id"]] = dataset["id"]
+                        update_dict[dataset] = dataset
                 # Collect SQL statements
                 statement += map.delete(dbif=dbif, update=False, execute=False)
         else:
@@ -170,4 +170,5 @@ def main():
 
 if __name__ == "__main__":
     options, flags = grass.parser()
-    main()
+    
+    tgis.profile_function(main)