浏览代码

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
     @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):
     def get_type(self):
         """!Return the type of this class as string
         """!Return the type of this class as string
 
 
@@ -224,6 +231,8 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
         self.temporal_extent.set_id(ident)
         self.temporal_extent.set_id(ident)
         self.spatial_extent.set_id(ident)
         self.spatial_extent.set_id(ident)
         self.metadata.set_id(ident)
         self.metadata.set_id(ident)
+        if self.is_stds() is False:
+            self.stds_register.set_id(ident)
 
 
     def get_id(self):
     def get_id(self):
         """!Return the unique identifier of the dataset
         """!Return the unique identifier of the dataset
@@ -257,23 +266,20 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
 
 
     def get_absolute_time(self):
     def get_absolute_time(self):
         """!Returns the start time, the end
         """!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 start time is of type datetime.
 
 
            The end time is of type datetime in case of interval time,
            The end time is of type datetime in case of interval time,
            or None on case of a time instance.
            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()
         start = self.absolute_time.get_start_time()
         end = self.absolute_time.get_end_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):
     def get_relative_time(self):
         """!Returns the start time, the end
         """!Returns the start time, the end
@@ -353,6 +359,8 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
         self.temporal_extent.select(dbif)
         self.temporal_extent.select(dbif)
         self.spatial_extent.select(dbif)
         self.spatial_extent.select(dbif)
         self.metadata.select(dbif)
         self.metadata.select(dbif)
+        if self.is_stds() is False:
+            self.stds_register.select(dbif)
 
 
         if connected:
         if connected:
             dbif.close()
             dbif.close()
@@ -379,7 +387,7 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
             @return The SQL insert statement in case execute=False, or an empty string otherwise
             @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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"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.temporal_extent.get_insert_statement_mogrified(dbif)
         statement += self.spatial_extent.get_insert_statement_mogrified(dbif)
         statement += self.spatial_extent.get_insert_statement_mogrified(dbif)
         statement += self.metadata.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:
         if execute:
             dbif.execute_transaction(statement)
             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
            @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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                                  {"ds":self.get_id(), "type":self.get_type()})
                                  {"ds":self.get_id(), "type":self.get_type()})
@@ -430,6 +440,9 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
                                                                         ident)
                                                                         ident)
         statement += self.metadata.get_update_statement_mogrified(dbif, 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:
         if execute:
             dbif.execute_transaction(statement)
             dbif.execute_transaction(statement)
             if connected:
             if connected:
@@ -452,7 +465,7 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
            @return The SQL update statement in case execute=False, or an empty string otherwise
            @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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -467,6 +480,9 @@ class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetCo
             dbif, ident)
             dbif, ident)
         statement += self.metadata.get_update_all_statement_mogrified(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:
         if execute:
             dbif.execute_transaction(statement)
             dbif.execute_transaction(statement)
             if connected:
             if connected:

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

@@ -15,7 +15,6 @@ for details.
 from abstract_dataset import *
 from abstract_dataset import *
 from datetime_math import *
 from datetime_math import *
 
 
-
 class AbstractMapDataset(AbstractDataset):
 class AbstractMapDataset(AbstractDataset):
     """!This is the base class for all maps (raster, vector, raster3d).
     """!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
            @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):
     def check_resolution_with_current_region(self):
         """!Check if the raster or voxel resolution is
         """!Check if the raster or voxel resolution is
            finer than the current resolution
            finer than the current resolution
@@ -141,7 +119,7 @@ class AbstractMapDataset(AbstractDataset):
         start = ""
         start = ""
 
 
         if self.is_time_absolute():
         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)
             start = datetime_to_grass_datetime_string(start_time)
             if end_time is not None:
             if end_time is not None:
                 end = datetime_to_grass_datetime_string(end_time)
                 end = datetime_to_grass_datetime_string(end_time)
@@ -212,6 +190,7 @@ class AbstractMapDataset(AbstractDataset):
         self.temporal_extent.print_self()
         self.temporal_extent.print_self()
         self.spatial_extent.print_self()
         self.spatial_extent.print_self()
         self.metadata.print_self()
         self.metadata.print_self()
+        self.stds_register.print_self()
 
 
     def print_info(self):
     def print_info(self):
         """!Print information about this object in human readable style"""
         """!Print information about this object in human readable style"""
@@ -244,9 +223,9 @@ class AbstractMapDataset(AbstractDataset):
                     string += "\n | ............................ "
                     string += "\n | ............................ "
                     count = 0
                     count = 0
                 if count == 0:
                 if count == 0:
-                    string += ds["id"]
+                    string += ds
                 else:
                 else:
-                    string += ",%s" % ds["id"]
+                    string += ",%s" % ds
                 count += 1
                 count += 1
         print " | Registered datasets ........ " + string
         print " | Registered datasets ........ " + string
         print " +----------------------------------------------------------------------------+"
         print " +----------------------------------------------------------------------------+"
@@ -263,9 +242,9 @@ class AbstractMapDataset(AbstractDataset):
         if datasets is not None:
         if datasets is not None:
             for ds in datasets:
             for ds in datasets:
                 if count == 0:
                 if count == 0:
-                    string += ds["id"]
+                    string += ds
                 else:
                 else:
-                    string += ",%s" % ds["id"]
+                    string += ",%s" % ds
                 count += 1
                 count += 1
             print "registered_datasets=" + string
             print "registered_datasets=" + string
 
 
@@ -278,7 +257,9 @@ class AbstractMapDataset(AbstractDataset):
 
 
            This functions assures that the timestamp is written to the
            This functions assures that the timestamp is written to the
            grass file system based database in addition to the temporal
            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 dbif The database interface to be used
            @param execute If True the SQL statements will be executed.
            @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
            @return The SQL insert statement in case execute=False, or an
                    empty string otherwise
                    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):
     def update(self, dbif=None, execute=True):
         """!Update the map content in the database from the internal structure
         """!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
            @return The SQL insert statement in case execute=False, or an
                    empty string otherwise
                    empty string otherwise
         """
         """
-        self.write_timestamp_to_grass()
+        if get_enable_timestamp_write():
+            self.write_timestamp_to_grass()
         return AbstractDataset.update(self, dbif, execute)
         return AbstractDataset.update(self, dbif, execute)
 
 
     def update_all(self, dbif=None, execute=True):
     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
             @return The SQL insert statement in case execute=False, or an
                     empty string otherwise
                     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)
         return AbstractDataset.update_all(self, dbif, execute)
 
 
     def set_time_to_absolute(self):
     def set_time_to_absolute(self):
@@ -334,7 +318,7 @@ class AbstractMapDataset(AbstractDataset):
         """!Set the temporal type to relative"""
         """!Set the temporal type to relative"""
         self.base.set_ttype("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
         """!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
             The end time is optional and must be set to None in case of time
@@ -347,7 +331,6 @@ class AbstractMapDataset(AbstractDataset):
                              map
                              map
            @param end_time a datetime object specifying the end time of the
            @param end_time a datetime object specifying the end time of the
                            map, None in case or time instance
                            map, None in case or time instance
-           @param timezone Thee timezone of the map (not used)
 
 
            @return True for success and False otherwise
            @return True for success and False otherwise
         """
         """
@@ -401,12 +384,10 @@ class AbstractMapDataset(AbstractDataset):
         self.base.set_ttype("absolute")
         self.base.set_ttype("absolute")
         self.absolute_time.set_start_time(start_time)
         self.absolute_time.set_start_time(start_time)
         self.absolute_time.set_end_time(end_time)
         self.absolute_time.set_end_time(end_time)
-        self.absolute_time.set_timezone(timezone)
 
 
         return True
         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
         """!Update the absolute time
 
 
            The end time is optional and must be set to None in case of 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
                   the map
            @param end_time a datetime object specifying the end time of the
            @param end_time a datetime object specifying the end time of the
                   map, None in case or time instance
                   map, None in case or time instance
-           @param timezone Thee timezone of the map (not used)
            @param dbif The database interface to be 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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"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)
             dbif, connected = init_dbif(dbif)
             self.absolute_time.update_all(dbif)
             self.absolute_time.update_all(dbif)
             self.base.update(dbif)
             self.base.update(dbif)
@@ -438,7 +418,8 @@ class AbstractMapDataset(AbstractDataset):
             if connected:
             if connected:
                 dbif.close()
                 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):
     def set_relative_time(self, start_time, end_time, unit):
         """!Set the relative time interval
         """!Set the relative time interval
@@ -514,7 +495,7 @@ class AbstractMapDataset(AbstractDataset):
            @param unit The relative time unit
            @param unit The relative time unit
            @param dbif The database interface to be 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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -527,7 +508,8 @@ class AbstractMapDataset(AbstractDataset):
             if connected:
             if connected:
                 dbif.close()
                 dbif.close()
 
 
-            self.write_timestamp_to_grass()
+            if get_enable_timestamp_write():
+                self.write_timestamp_to_grass()
 
 
     def set_temporal_extent(self, extent):
     def set_temporal_extent(self, extent):
         """!Convenient method to set the temporal extent from a temporal extent object
         """!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))
            (datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(2001, 1, 1, 0, 0))
 
 
            >>> map1 = tgis.VectorDataset("A@P")
            >>> 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()
            >>> print map1.get_temporal_extent_as_tuple()
            (datetime.datetime(2000, 5, 5, 0, 0), datetime.datetime(2005, 6, 6, 0, 0))
            (datetime.datetime(2000, 5, 5, 0, 0), datetime.datetime(2005, 6, 6, 0, 0))
            >>> map2 = tgis.RasterDataset("B@P")
            >>> 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()
            >>> print map2.get_temporal_extent_as_tuple()
            (datetime.datetime(1990, 1, 1, 0, 0), datetime.datetime(1999, 8, 1, 0, 0))
            (datetime.datetime(1990, 1, 1, 0, 0), datetime.datetime(1999, 8, 1, 0, 0))
            >>> map2.set_temporal_extent(map1.get_temporal_extent())
            >>> map2.set_temporal_extent(map1.get_temporal_extent())
@@ -573,9 +555,8 @@ class AbstractMapDataset(AbstractDataset):
         elif issubclass(type(extent), AbsoluteTemporalExtent):
         elif issubclass(type(extent), AbsoluteTemporalExtent):
             start = extent.get_start_time()
             start = extent.get_start_time()
             end = extent.get_end_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):
     def temporal_buffer(self, increment, update=False, dbif=None):
         """!Create a temporal buffer based on an increment
         """!Create a temporal buffer based on an increment
@@ -655,7 +636,7 @@ class AbstractMapDataset(AbstractDataset):
         """
         """
 
 
         if self.is_time_absolute():
         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)
             new_start = decrement_datetime_by_string(start, increment)
             if end == None:
             if end == None:
@@ -664,9 +645,9 @@ class AbstractMapDataset(AbstractDataset):
                 new_end = increment_datetime_by_string(end, increment)
                 new_end = increment_datetime_by_string(end, increment)
 
 
             if update:
             if update:
-                self.update_absolute_time(new_start, new_end, tz, dbif=dbif)
+                self.update_absolute_time(new_start, new_end, dbif=dbif)
             else:
             else:
-                self.set_absolute_time(new_start, new_end, tz)
+                self.set_absolute_time(new_start, new_end)
         else:
         else:
             start, end, unit = self.get_relative_time()
             start, end, unit = self.get_relative_time()
             new_start = start - increment
             new_start = start - increment
@@ -774,7 +755,7 @@ class AbstractMapDataset(AbstractDataset):
     def check_for_correct_time(self):
     def check_for_correct_time(self):
         """!Check for correct time"""
         """!Check for correct time"""
         if self.is_time_absolute():
         if self.is_time_absolute():
-            start, end, tz = self.get_absolute_time()
+            start, end= self.get_absolute_time()
         else:
         else:
             start, end, unit = self.get_relative_time()
             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,
            @return The SQL statements if execute=False, else an empty string,
                    None in case of a failure
                    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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -837,11 +818,6 @@ class AbstractMapDataset(AbstractDataset):
             statement += self.unregister(
             statement += self.unregister(
                 dbif=dbif, update=update, execute=False)
                 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.msgr.verbose(_("Delete %s dataset <%s> from temporal database")
                          % (self.get_type(), self.get_id()))
                          % (self.get_type(), self.get_id()))
 
 
@@ -851,6 +827,7 @@ class AbstractMapDataset(AbstractDataset):
 
 
         if execute:
         if execute:
             dbif.execute_transaction(statement)
             dbif.execute_transaction(statement)
+            statement = ""
 
 
         # Remove the timestamp from the file system
         # Remove the timestamp from the file system
         self.remove_timestamp_from_grass()
         self.remove_timestamp_from_grass()
@@ -860,9 +837,6 @@ class AbstractMapDataset(AbstractDataset):
         if connected:
         if connected:
             dbif.close()
             dbif.close()
 
 
-        if execute:
-            return ""
-
         return statement
         return statement
 
 
     def unregister(self, dbif=None, update=True, execute=True):
     def unregister(self, dbif=None, update=True, execute=True):
@@ -883,16 +857,16 @@ class AbstractMapDataset(AbstractDataset):
 
 
 
 
         if self.get_layer() is not None:
         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" % \
                            "layer %(layer)s from space time datasets" % \
                          {'type':self.get_type(), 'map':self.get_map_id(),
                          {'type':self.get_type(), 'map':self.get_map_id(),
-                          'layer':self.get_layer()}))
+                          'layer':self.get_layer()})
         else:
         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"
                            "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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -901,59 +875,143 @@ class AbstractMapDataset(AbstractDataset):
         dbif, connected = init_dbif(dbif)
         dbif, connected = init_dbif(dbif)
 
 
         # Get all datasets in which this map is registered
         # 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
         # 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:
         if execute:
             dbif.execute_transaction(statement)
             dbif.execute_transaction(statement)
+            statement = ""
 
 
         if connected:
         if connected:
             dbif.close()
             dbif.close()
 
 
-        if execute:
-            return ""
-
         return statement
         return statement
 
 
     def get_registered_datasets(self, dbif=None):
     def get_registered_datasets(self, dbif=None):
         """!Return all space time dataset ids in which this map is registered
         """!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.
            registered in any space time dataset.
 
 
            @param dbif The database interface to be used
            @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
                    which this map is registered
         """
         """
         dbif, connected = init_dbif(dbif)
         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:
         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()
         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):
                            title=None, description=None):
         """!Set the initial values of the space time dataset
         """!Set the initial values of the space time dataset
 
 
@@ -157,6 +157,13 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         self.metadata.set_description(description)
         self.metadata.set_description(description)
         self.metadata.set_command(self.create_command_string())
         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):
     def update_command_string(self, dbif=None):
         """!Append the current command string to any existing command string
         """!Append the current command string to any existing command string
            in the metadata class and calls metadata update
            in the metadata class and calls metadata update
@@ -296,6 +303,70 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                 self.msgr.fatal(_("Unsupported temporal unit: %s") % (unit))
                 self.msgr.fatal(_("Unsupported temporal unit: %s") % (unit))
             self.relative_time.set_unit(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):
     def get_map_time(self):
         """!Return the type of the map time, interval, point, mixed or invalid
         """!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)):
         for i in range(len(maps)):
             # Check for point and interval data
             # Check for point and interval data
             if maps[i].is_time_absolute():
             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():
             if maps[i].is_time_relative():
                 start, end, unit = maps[i].get_relative_time()
                 start, end, unit = maps[i].get_relative_time()
 
 
@@ -1131,7 +1202,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             map = first.get_new_instance(None)
             map = first.get_new_instance(None)
             map.set_spatial_extent_from_values(0,0,0,0,0,0)
             map.set_spatial_extent_from_values(0,0,0,0,0,0)
             if first.is_time_absolute():
             if first.is_time_absolute():
-                map.set_absolute_time(start, next, None)
+                map.set_absolute_time(start, next)
             else:
             else:
                 map.set_relative_time(start, next, first.get_relative_time_unit())
                 map.set_relative_time(start, next, first.get_relative_time_unit())
 
 
@@ -1495,7 +1566,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                    granularity
                    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."
             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")%\
             " The mapset of the dataset does not match the current mapset")%\
             ({"ds":self.get_id()}, {"type":self.get_type()}))
             ({"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."
             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")%\
             " The mapset of the dataset does not match the current mapset")%\
             ({"ds":self.get_id()}, {"type":self.get_type()}))
             ({"ds":self.get_id()}, {"type":self.get_type()}))
@@ -1738,7 +1809,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             datasets = map.get_registered_datasets(dbif)
             datasets = map.get_registered_datasets(dbif)
             if datasets:
             if datasets:
                 for dataset in datasets:
                 for dataset in datasets:
-                    datatsets_to_modify[dataset["id"]] = dataset["id"]
+                    datatsets_to_modify[dataset] = dataset
 
 
         self.update_from_registered_maps(dbif)
         self.update_from_registered_maps(dbif)
 
 
@@ -1762,13 +1833,16 @@ class AbstractSpaceTimeDataset(AbstractDataset):
            @param dbif The database interface to be 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 rename dataset <%(ds)s> of type %(type)s in the temporal database."
             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")%\
             " The mapset of the dataset does not match the current mapset")%\
             ({"ds":self.get_id()}, {"type":self.get_type()}))
             ({"ds":self.get_id()}, {"type":self.get_type()}))
 
 
         dbif, connected = init_dbif(dbif)
         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
         # SELECT all needed information from the database
         self.select(dbif)
         self.select(dbif)
 
 
@@ -1790,16 +1864,22 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         # Get the update statement, we update the table entry of the old
         # Get the update statement, we update the table entry of the old
         # identifier
         # identifier
         statement = self.update(dbif, execute=False, ident=old_ident)
         statement = self.update(dbif, execute=False, ident=old_ident)
+
         # We need to rename the raster register table
         # 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
         # We need to rename the space time dataset in the maps register table
         if maps:
         if maps:
             for map in 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
         # Execute the accumulated statements
         dbif.execute_transaction(statement)
         dbif.execute_transaction(statement)
@@ -1829,7 +1909,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                     (self.get_new_map_instance(ident=None).get_type(),
                     (self.get_new_map_instance(ident=None).get_type(),
                      self.get_id()))
                      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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -1871,6 +1951,42 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
 
         return statement
         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):
     def register_map(self, map, dbif=None):
         """!Register a map in the space time dataset.
         """!Register a map in the space time dataset.
 
 
@@ -1880,14 +1996,14 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             In case the map is already registered this function
             In case the map is already registered this function
             will break with a warning and return False.
             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 map The AbstractMapDataset object that should be registered
            @param dbif The database interface to be used
            @param dbif The database interface to be used
            @return True if success, False otherwise
            @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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -1896,19 +2012,19 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
 
         if map.is_in_db(dbif) == False:
         if map.is_in_db(dbif) == False:
             dbif.close()
             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():
         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:
         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()))
                                               map.get_type(), self.get_id()))
 
 
         # First select all data from the database
         # First select all data from the database
@@ -1921,17 +2037,12 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             else:
             else:
                 self.msgr.fatal(_("Map <%s> has invalid time") % (map.get_map_id()))
                 self.msgr.fatal(_("Map <%s> has invalid time") % (map.get_map_id()))
 
 
+        # Get basic info
         map_id = map.base.get_id()
         map_id = map.base.get_id()
-        map_name = map.base.get_name()
         map_mapset = map.base.get_mapset()
         map_mapset = map.base.get_mapset()
-        map_register_table = map.get_stds_register()
         map_rel_time_unit = map.get_relative_time_unit()
         map_rel_time_unit = map.get_relative_time_unit()
         map_ttype = map.get_temporal_type()
         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_mapset = self.base.get_mapset()
         stds_register_table = self.get_map_register()
         stds_register_table = self.get_map_register()
         stds_ttype = self.get_temporal_type()
         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"))
             self.msgr.fatal(_("Only maps from the same mapset can be registered"))
 
 
         # Check if map is already 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:
             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
         # Now put the raster name in the stds map register table
         if dbif.dbmi.paramstyle == "qmark":
         if dbif.dbmi.paramstyle == "qmark":
             sql = "INSERT INTO " + stds_register_table + \
             sql = "INSERT INTO " + stds_register_table + \
@@ -2114,6 +2117,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         else:
         else:
             sql = "INSERT INTO " + stds_register_table + \
             sql = "INSERT INTO " + stds_register_table + \
                 " (id) " + "VALUES (%s);\n"
                 " (id) " + "VALUES (%s);\n"
+                
 
 
         statement += dbif.mogrify_sql_statement((sql, (map_id,)))
         statement += dbif.mogrify_sql_statement((sql, (map_id,)))
 
 
@@ -2145,7 +2149,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                    string, None in case of a failure
                    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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -2154,53 +2158,29 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
 
         dbif, connected = init_dbif(dbif)
         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
         # 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:
             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 stds_register_table is not None:
             if dbif.dbmi.paramstyle == "qmark":
             if dbif.dbmi.paramstyle == "qmark":
                 sql = "DELETE FROM " + stds_register_table + " WHERE id = ?;\n"
                 sql = "DELETE FROM " + stds_register_table + " WHERE id = ?;\n"
@@ -2208,10 +2188,11 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                 sql = "DELETE FROM " + \
                 sql = "DELETE FROM " + \
                     stds_register_table + " WHERE id = %s;\n"
                     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:
         if execute:
             dbif.execute_transaction(statement)
             dbif.execute_transaction(statement)
+            statement = ""
 
 
         if connected:
         if connected:
             dbif.close()
             dbif.close()
@@ -2219,13 +2200,10 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         # decrease the counter
         # decrease the counter
         self.map_counter -= 1
         self.map_counter -= 1
 
 
-        if execute:
-            return ""
-
         return statement
         return statement
 
 
     def update_from_registered_maps(self, dbif=None):
     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
            type specific metadata. It should always been called after maps
            are registered or unregistered/deleted from the space time dataset.
            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
            will be used. If the end time is earlier than the maximum start
            time, it will be replaced by the maximum start time.
            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
            @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."
             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")%\
                          " The mapset of the dataset does not match the current mapset")%\
                          {"ds":self.get_id(), "type":self.get_type()})
                          {"ds":self.get_id(), "type":self.get_type()})
@@ -2301,7 +2275,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         self.select()
         self.select()
 
 
         if self.is_time_absolute():
         if self.is_time_absolute():
-            start_time, end_time, tz = self.get_absolute_time()
+            start_time, end_time = self.get_absolute_time()
         else:
         else:
             start_time, end_time, unit = self.get_relative_time()
             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_map_time(None)
             self.temporal_extent.set_granularity(None)
             self.temporal_extent.set_granularity(None)
         self.temporal_extent.update_all(dbif)
         self.temporal_extent.update_all(dbif)
+        
+        # Set the modification time
+        self.base.set_mtime(datetime.now())
+        self.base.update(dbif)
 
 
         if connected:
         if connected:
             dbif.close()
             dbif.close()

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

@@ -523,7 +523,6 @@ class SQLDatabaseInterface(DictSQLSerializer):
 
 
 ###############################################################################
 ###############################################################################
 
 
-
 class DatasetBase(SQLDatabaseInterface):
 class DatasetBase(SQLDatabaseInterface):
     """!This is the base class for all maps and spacetime datasets storing
     """!This is the base class for all maps and spacetime datasets storing
         basic identification information
         basic identification information
@@ -552,15 +551,15 @@ class DatasetBase(SQLDatabaseInterface):
          | Name: ...................... soil
          | Name: ...................... soil
          | Mapset: .................... PERMANENT
          | Mapset: .................... PERMANENT
          | Creator: ................... soeren
          | Creator: ................... soeren
-         | Creation time: ............. 2001-01-01 00:00:00
          | Temporal type: ............. absolute
          | Temporal type: ............. absolute
+         | Creation time: ............. 2001-01-01 00:00:00
         >>> t.print_shell_info()
         >>> t.print_shell_info()
         id=soil@PERMANENT
         id=soil@PERMANENT
         name=soil
         name=soil
         mapset=PERMANENT
         mapset=PERMANENT
         creator=soeren
         creator=soeren
-        creation_time=2001-01-01 00:00:00
         temporal_type=absolute
         temporal_type=absolute
+        creation_time=2001-01-01 00:00:00
 
 
         \endcode
         \endcode
     """
     """
@@ -597,9 +596,6 @@ class DatasetBase(SQLDatabaseInterface):
         self.set_creator(creator)
         self.set_creator(creator)
         self.set_ctime(ctime)
         self.set_ctime(ctime)
         self.set_ttype(ttype)
         self.set_ttype(ttype)
-        # Commented out for performance reasons
-        #self.set_mtime(mtime)
-        #self.set_revision(revision)
 
 
     def set_id(self, ident):
     def set_id(self, ident):
         """!Convenient method to set the unique identifier (primary key)
         """!Convenient method to set the unique identifier (primary key)
@@ -676,17 +672,6 @@ class DatasetBase(SQLDatabaseInterface):
         else:
         else:
             self.D["temporal_type"] = ttype
             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):
     def get_id(self):
         """!Convenient method to get the unique identifier (primary key)
         """!Convenient method to get the unique identifier (primary key)
 
 
@@ -766,22 +751,6 @@ class DatasetBase(SQLDatabaseInterface):
         else:
         else:
             return None
             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
     # Properties of this class
     id = property(fget=get_id, fset=set_id)
     id = property(fget=get_id, fset=set_id)
     map_id = property(fget=get_map_id, fset=None)
     map_id = property(fget=get_map_id, fset=None)
@@ -801,10 +770,8 @@ class DatasetBase(SQLDatabaseInterface):
         if self.get_layer():
         if self.get_layer():
             print " | Layer:...................... " + str(self.get_layer())
             print " | Layer:...................... " + str(self.get_layer())
         print " | Creator: ................... " + str(self.get_creator())
         print " | Creator: ................... " + str(self.get_creator())
-        print " | Creation time: ............. " + str(self.get_ctime())
         print " | Temporal type: ............. " + str(self.get_ttype())
         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):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!Print information about this class in shell style"""
@@ -814,10 +781,8 @@ class DatasetBase(SQLDatabaseInterface):
         if self.get_layer():
         if self.get_layer():
             print "layer=" + str(self.get_layer())
             print "layer=" + str(self.get_layer())
         print "creator=" + str(self.get_creator())
         print "creator=" + str(self.get_creator())
-        print "creation_time=" + str(self.get_ctime())
         print "temporal_type=" + str(self.get_ttype())
         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
     \code
 
 
     >>> init()
     >>> init()
-    >>> t = STDSBase("stds", "soil@PERMANENT", semantic_type="average", creator="soeren", ctime=datetime(2001,1,1), ttype="absolute")
+    >>> t = STDSBase("stds", "soil@PERMANENT", semantic_type="average", creator="soeren", ctime=datetime(2001,1,1), ttype="absolute", mtime=datetime(2001,1,1))
     >>> t.semantic_type
     >>> t.semantic_type
     'average'
     'average'
     >>> t.print_info()
     >>> t.print_info()
@@ -881,31 +846,41 @@ class STDSBase(DatasetBase):
      | Name: ...................... soil
      | Name: ...................... soil
      | Mapset: .................... PERMANENT
      | Mapset: .................... PERMANENT
      | Creator: ................... soeren
      | Creator: ................... soeren
-     | Creation time: ............. 2001-01-01 00:00:00
      | Temporal type: ............. absolute
      | Temporal type: ............. absolute
+     | Creation time: ............. 2001-01-01 00:00:00
+     | Modification time:.......... 2001-01-01 00:00:00
      | Semantic type:.............. average
      | Semantic type:.............. average
     >>> t.print_shell_info()
     >>> t.print_shell_info()
     id=soil@PERMANENT
     id=soil@PERMANENT
     name=soil
     name=soil
     mapset=PERMANENT
     mapset=PERMANENT
     creator=soeren
     creator=soeren
-    creation_time=2001-01-01 00:00:00
     temporal_type=absolute
     temporal_type=absolute
+    creation_time=2001-01-01 00:00:00
+    modification_time=2001-01-01 00:00:00
     semantic_type=average
     semantic_type=average
 
 
     \endcode
     \endcode
     """
     """
     def __init__(self, table=None, ident=None, name=None, mapset=None,
     def __init__(self, table=None, ident=None, name=None, mapset=None,
                  semantic_type=None, creator=None, ctime=None,
                  semantic_type=None, creator=None, ctime=None,
-                 ttype=None):
+                 ttype=None, mtime=None):
         DatasetBase.__init__(self, table, ident, name, mapset, creator,
         DatasetBase.__init__(self, table, ident, name, mapset, creator,
                               ctime, ttype)
                               ctime, ttype)
 
 
         self.set_semantic_type(semantic_type)
         self.set_semantic_type(semantic_type)
+        self.set_mtime(mtime)
 
 
     def set_semantic_type(self, semantic_type):
     def set_semantic_type(self, semantic_type):
         """!Set the semantic type of the space time dataset"""
         """!Set the semantic type of the space time dataset"""
         self.D["semantic_type"] = semantic_type
         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):
     def get_semantic_type(self):
         """!Get the semantic type of the space time dataset
         """!Get the semantic type of the space time dataset
@@ -914,6 +889,14 @@ class STDSBase(DatasetBase):
             return self.D["semantic_type"]
             return self.D["semantic_type"]
         else:
         else:
             return None
             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)
     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"""
         """!Print information about this class in human readable style"""
         DatasetBase.print_info(self)
         DatasetBase.print_info(self)
         #      0123456789012345678901234567890
         #      0123456789012345678901234567890
+        print " | Modification time:.......... " + str(self.get_mtime())
         print " | Semantic type:.............. " + str(
         print " | Semantic type:.............. " + str(
             self.get_semantic_type())
             self.get_semantic_type())
 
 
     def print_shell_info(self):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!Print information about this class in shell style"""
         DatasetBase.print_shell_info(self)
         DatasetBase.print_shell_info(self)
+        print "modification_time=" + str(self.get_mtime())
         print "semantic_type=" + str(self.get_semantic_type())
         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__":
 if __name__ == "__main__":
     import doctest
     import doctest
     doctest.testmod()
     doctest.testmod()

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

@@ -34,7 +34,13 @@ for details.
 
 
 @author Soeren Gebbert
 @author Soeren Gebbert
 """
 """
+import sys, traceback
 import os
 import os
+import locale
+# i18N
+import gettext
+gettext.install('grasslibs', os.path.join(os.getenv("GISBASE"), 'locale'))
+
 import grass.script.core as core
 import grass.script.core as core
 from datetime import datetime
 from datetime import datetime
 from c_libraries_interface import *
 from c_libraries_interface import *
@@ -51,8 +57,25 @@ try:
 except:
 except:
     pass
     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
 # Global variable that defines the backend
 # of the temporal GIS
 # of the temporal GIS
@@ -62,12 +85,12 @@ tgis_backend = None
 # The version of the temporal framework
 # The version of the temporal framework
 # this value must be an integer larger than 0
 # this value must be an integer larger than 0
 # Increase this value in case of backward incompatible changes in the TGIS API
 # 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
 # The version of the temporal database since framework and database version can differ
 # this value must be an integer larger than 0
 # this value must be an integer larger than 0
 # Increase this value in case of backward incompatible changes
 # Increase this value in case of backward incompatible changes
 # temporal database SQL layout
 # 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
 # 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
 # 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
 # The global variable that stores the PyGRASS Messenger object that
 # provides a fast and exit safe interface to the C-library message functions
 # provides a fast and exit safe interface to the C-library message functions
 message_interface=None
 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):
 def get_tgis_metadata(dbif=None):
     """!Return the tgis metadata table as a list of rows (dicts)
     """!Return the tgis metadata table as a list of rows (dicts)
                or None if not present
                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 kv dictionary generated by grass.script.parse_command("t.connect", flags="pg")
        @param grassenv Grass environemntal variables created by grass.script.gisenv()
        @param grassenv Grass environemntal variables created by grass.script.gisenv()
     """
     """
+
+    global temporal_dbmi_init_string
+
     if kv == None:
     if kv == None:
         kv = core.parse_command("t.connect", flags="pg")
         kv = core.parse_command("t.connect", flags="pg")
     if grassenv == None:
     if grassenv == None:
@@ -207,6 +290,7 @@ def get_temporal_dbmi_init_string(kv=None, grassenv=None):
             string = string.replace(
             string = string.replace(
                 "$LOCATION_NAME", grassenv["LOCATION_NAME"])
                 "$LOCATION_NAME", grassenv["LOCATION_NAME"])
             string = string.replace("$MAPSET", grassenv["MAPSET"])
             string = string.replace("$MAPSET", grassenv["MAPSET"])
+            temporal_dbmi_init_string = string
             return string
             return string
         else:
         else:
             msgr.fatal(_("Unable to initialize the temporal GIS DBMI "
             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":
     elif tgis_backend == "pg":
         if "database" in kv:
         if "database" in kv:
             string = kv["database"]
             string = kv["database"]
+            temporal_dbmi_init_string = string
             return string
             return string
     else:
     else:
         msgr.fatal(_("Unable to initialize the temporal GIS DBMI "
         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,
        and creates the grass location database structure for raster,
        vector and raster3d maps as well as for the space-time datasets strds,
        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
         ATTENTION: This functions must be called before any spatio-temporal processing
                    can be started
                    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
     # We need to set the correct database backend from the environment variables
     global tgis_backend
     global tgis_backend
-
+    global enable_mapset_check
+    global enable_timestamp_write
+    
     core.run_command("t.connect", flags="c")
     core.run_command("t.connect", flags="c")
     kv = core.parse_command("t.connect", flags="pg")
     kv = core.parse_command("t.connect", flags="pg")
     grassenv = core.gisenv()
     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 the global variable current_mapset for fast mapset access
     _set_current_mapset(grassenv["MAPSET"])
     _set_current_mapset(grassenv["MAPSET"])
     # Start the GRASS message interface server
     # Start the GRASS message interface server
     _init_tgis_message_interface(raise_on_error)
     _init_tgis_message_interface(raise_on_error)
     # Start the C-library interface server
     # Start the C-library interface server
     _init_tgis_c_library_interface()
     _init_tgis_c_library_interface()
-
+    
     msgr = get_tgis_message_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 "driver" in kv:
         if kv["driver"] == "sqlite":
         if kv["driver"] == "sqlite":
@@ -309,35 +424,24 @@ def init(raise_on_error=False):
         if dbif.cursor.fetchone()[0]:
         if dbif.cursor.fetchone()[0]:
             db_exists = True
             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:
     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()
         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
         return
 
 
     create_temporal_database(dbif, database)
     create_temporal_database(dbif, database)
@@ -415,10 +519,19 @@ def create_temporal_database(dbif, database):
         tgis_dir = os.path.dirname(database)
         tgis_dir = os.path.dirname(database)
         if not os.path.exists(tgis_dir):
         if not os.path.exists(tgis_dir):
             os.makedirs(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
     # Connect now to the database
     if not dbif.connected:
     if not dbif.connected:
@@ -446,8 +559,10 @@ def create_temporal_database(dbif, database):
     dbif.execute_transaction(str3ds_metadata_sql)
     dbif.execute_transaction(str3ds_metadata_sql)
     dbif.execute_transaction(str3ds_views_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
     # Create the tgis metadata table to store the database
     # initial configuration
     # initial configuration
@@ -455,7 +570,6 @@ def create_temporal_database(dbif, database):
     metadata = {}
     metadata = {}
     metadata["tgis_version"] = tgis_version
     metadata["tgis_version"] = tgis_version
     metadata["tgis_db_version"] = tgis_db_version
     metadata["tgis_db_version"] = tgis_db_version
-    metadata["has_command_column"] = True
     metadata["creation_time"] = datetime.today()
     metadata["creation_time"] = datetime.today()
     _create_tgis_metadata_table(metadata, dbif)
     _create_tgis_metadata_table(metadata, dbif)
 
 
@@ -502,7 +616,7 @@ class SQLDatabaseInterfaceConnection():
         else:
         else:
             self.dbmi = psycopg2
             self.dbmi = psycopg2
 
 
-        msgr = get_tgis_message_interface()
+        self.msgr = get_tgis_message_interface()
 
 
     def rollback(self):
     def rollback(self):
         """
         """
@@ -521,7 +635,6 @@ class SQLDatabaseInterfaceConnection():
            Supported backends are sqlite3 and postgresql
            Supported backends are sqlite3 and postgresql
         """
         """
         self.database = get_temporal_dbmi_init_string()
         self.database = get_temporal_dbmi_init_string()
-        #print "Connect to",  self.database
         if self.dbmi.__name__ == "sqlite3":
         if self.dbmi.__name__ == "sqlite3":
             self.connection = self.dbmi.connect(self.database,
             self.connection = self.dbmi.connect(self.database,
                     detect_types = self.dbmi.PARSE_DECLTYPES | self.dbmi.PARSE_COLNAMES)
                     detect_types = self.dbmi.PARSE_DECLTYPES | self.dbmi.PARSE_COLNAMES)
@@ -680,8 +793,7 @@ class SQLDatabaseInterfaceConnection():
         except:
         except:
             if connected:
             if connected:
                 self.close()
                 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
             raise
 
 
         if connected:
         if connected:

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

@@ -15,6 +15,12 @@ from datetime import datetime, date, time, timedelta
 from core import *
 from core import *
 import copy
 import copy
 
 
+try:
+    import dateutil.parser as parser
+    has_dateutil = True
+except:
+    has_dateutil = False
+
 DAY_IN_SECONDS = 86400
 DAY_IN_SECONDS = 86400
 SECOND_AS_DAY = 1.1574074074074073e-05
 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
         @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
     # BC is not supported
     if time_string.find("bc") > 0:
     if time_string.find("bc") > 0:
         return _("Dates Before Christ are not supported")
         return _("Dates Before Christ are not supported")
@@ -732,11 +756,13 @@ def check_datetime_string(time_string):
 def string_to_datetime(time_string):
 def string_to_datetime(time_string):
     """!Convert a string into a datetime object
     """!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
         - YYYY-mm-dd HH:MM:SS
         - 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
         @param time_string The time string to convert
         @return datetime object or None in case the string 
         @return datetime object or None in case the string 
@@ -754,23 +780,47 @@ def string_to_datetime(time_string):
 
 
     return time_object
     return time_object
 
 
-
-
 ###############################################################################
 ###############################################################################
 
 
 
 
 def datetime_to_grass_datetime_string(dt):
 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
     # GRASS datetime month names
     month_names = ["", "jan", "feb", "mar", "apr", "may", "jun",
     month_names = ["", "jan", "feb", "mar", "apr", "may", "jun",
                    "jul", "aug", "sep", "oct", "nov", "dec"]
                    "jul", "aug", "sep", "oct", "nov", "dec"]
 
 
     # Check for time zone info in the datetime object
     # Check for time zone info in the datetime object
     if dt.tzinfo is not None:
     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,
         string = "%.2i %s %.2i %.2i:%.2i:%.2i %+.4i" % (dt.day,
                  month_names[dt.month], dt.year,
                  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:
     else:
         string = "%.2i %s %.4i %.2i:%.2i:%.2i" % (dt.day, month_names[
         string = "%.2i %s %.4i %.2i:%.2i:%.2i" % (dt.day, month_names[
             dt.month], dt.year, dt.hour, dt.minute, dt.second)
             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
                                 continue
 
 
                     # Set the time stamp
                     # 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
                     # Insert map in temporal database
                     new_map.insert(dbif)
                     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
             # Set the time stamp
             if sample_map_list[i].is_time_absolute():
             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:
             else:
                 start, end, unit = sample_map_list[i].get_relative_time()
                 start, end, unit = sample_map_list[i].get_relative_time()
                 new_map.set_relative_time(start, end, unit)
                 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_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()
     msgr = get_tgis_message_interface()
 
 
     if expr.find("start_year()") >= 0:
     if expr.find("start_year()") >= 0:
@@ -505,7 +505,7 @@ def _parse_end_operators(expr, is_time_absolute, current):
        null()
        null()
     """
     """
 
 
-    start, end, tz = current.get_absolute_time()
+    start, end = current.get_absolute_time()
     msgr = get_tgis_message_interface()
     msgr = get_tgis_message_interface()
 
 
     if expr.find("end_year()") >= 0:
     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:
     if expr.find("td()") >= 0:
         td = "null()"
         td = "null()"
         if is_time_absolute:
         if is_time_absolute:
-            start, end, tz = current.get_absolute_time()
+            start, end = current.get_absolute_time()
             if end != None:
             if end != None:
                 td = time_delta_to_relative_time(end - start)
                 td = time_delta_to_relative_time(end - start)
         else:
         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."""
     time, and in relative units in case of relative time."""
     if expr.find("start_time()") >= 0:
     if expr.find("start_time()") >= 0:
         if is_time_absolute:
         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)
             x = time_delta_to_relative_time(start - start1)
         else:
         else:
             start1, end, unit = first.get_relative_time()
             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:
     if expr.find("end_time()") >= 0:
         x = "null()"
         x = "null()"
         if is_time_absolute:
         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:
             if end:
                 x = time_delta_to_relative_time(end - start1)
                 x = time_delta_to_relative_time(end - start1)
         else:
         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 metadata includes the datatype, number of cols, rows and cells and
         the north-south and east west resolution of the map. Additionally the
         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:
         Usage:
 
 
@@ -298,7 +297,6 @@ class RasterMetadata(RasterMetadataBase):
         0.0
         0.0
         >>> meta.max
         >>> meta.max
         100.0
         100.0
-        >>> meta.strds_register
         >>> meta.print_info()
         >>> meta.print_info()
          +-------------------- Metadata information ----------------------------------+
          +-------------------- Metadata information ----------------------------------+
          | Datatype:................... CELL
          | Datatype:................... CELL
@@ -309,7 +307,6 @@ class RasterMetadata(RasterMetadataBase):
          | East-west resolution:....... 0.1
          | East-west resolution:....... 0.1
          | Minimum value:.............. 0.0
          | Minimum value:.............. 0.0
          | Maximum value:.............. 100.0
          | Maximum value:.............. 100.0
-         | STRDS register table ....... None
         >>> meta.print_shell_info()
         >>> meta.print_shell_info()
         datatype=CELL
         datatype=CELL
         cols=100
         cols=100
@@ -319,11 +316,10 @@ class RasterMetadata(RasterMetadataBase):
         ewres=0.1
         ewres=0.1
         min=0.0
         min=0.0
         max=100.0
         max=100.0
-        strds_register=None
 
 
         @endcode
         @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,
 		 cols=None, rows=None, number_of_cells=None, nsres=None,
 		 ewres=None, min=None, max=None):
 		 ewres=None, min=None, max=None):
 
 
@@ -331,34 +327,15 @@ class RasterMetadata(RasterMetadataBase):
                                       cols, rows, number_of_cells, nsres,
                                       cols, rows, number_of_cells, nsres,
                                       ewres, min, max)
                                       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):
     def print_info(self):
         """!Print information about this class in human readable style"""
         """!Print information about this class in human readable style"""
         print " +-------------------- Metadata information ----------------------------------+"
         print " +-------------------- Metadata information ----------------------------------+"
         #      0123456789012345678901234567890
         #      0123456789012345678901234567890
         RasterMetadataBase.print_info(self)
         RasterMetadataBase.print_info(self)
-        print " | STRDS register table ....... " + str(
-            self.get_strds_register())
 
 
     def print_shell_info(self):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!Print information about this class in shell style"""
         RasterMetadataBase.print_shell_info(self)
         RasterMetadataBase.print_shell_info(self)
-        print "strds_register=" + str(self.get_strds_register())
 
 
 ###############################################################################
 ###############################################################################
 
 
@@ -403,7 +380,6 @@ class Raster3DMetadata(RasterMetadataBase):
         0.0
         0.0
         >>> meta.max
         >>> meta.max
         100.0
         100.0
-        >>> meta.str3ds_register
         >>> meta.print_info()
         >>> meta.print_info()
          +-------------------- Metadata information ----------------------------------+
          +-------------------- Metadata information ----------------------------------+
          | Datatype:................... FCELL
          | Datatype:................... FCELL
@@ -416,7 +392,6 @@ class Raster3DMetadata(RasterMetadataBase):
          | Maximum value:.............. 100.0
          | Maximum value:.............. 100.0
          | Number of depths:........... 100
          | Number of depths:........... 100
          | Top-Bottom resolution:...... 0.1
          | Top-Bottom resolution:...... 0.1
-         | STR3DS register table ...... None
         >>> meta.print_shell_info()
         >>> meta.print_shell_info()
         datatype=FCELL
         datatype=FCELL
         cols=100
         cols=100
@@ -426,13 +401,12 @@ class Raster3DMetadata(RasterMetadataBase):
         ewres=0.1
         ewres=0.1
         min=0.0
         min=0.0
         max=100.0
         max=100.0
-        str3ds_register=None
         depths=100
         depths=100
         tbres=0.1
         tbres=0.1
 
 
         @endcode
         @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,
 		 cols=None, rows=None, depths=None, number_of_cells=None,
 		 nsres=None, ewres=None, tbres=None, min=None, max=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,
 				datatype, cols, rows, number_of_cells, nsres,
 				ewres, min, max)
 				ewres, min, max)
 
 
-        self.set_str3ds_register(str3ds_register)
         self.set_tbres(tbres)
         self.set_tbres(tbres)
         self.set_depths(depths)
         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):
     def set_depths(self, depths):
         """!Set the number of depths"""
         """!Set the number of depths"""
         if depths is not None:
         if depths is not None:
@@ -462,14 +431,6 @@ class Raster3DMetadata(RasterMetadataBase):
         else:
         else:
             self.D["tbres"] = None
             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):
     def get_depths(self):
         """!Get number of depths
         """!Get number of depths
            @return None if not found"""
            @return None if not found"""
@@ -488,7 +449,6 @@ class Raster3DMetadata(RasterMetadataBase):
 
 
     depths = property(fget=get_depths, fset=set_depths)
     depths = property(fget=get_depths, fset=set_depths)
     tbres = property(fget=get_tbres, fset=set_tbres)
     tbres = property(fget=get_tbres, fset=set_tbres)
-    str3ds_register = property(fget=get_str3ds_register, fset=set_str3ds_register)
 
 
     def print_info(self):
     def print_info(self):
         """!Print information about this class in human readable style"""
         """!Print information about this class in human readable style"""
@@ -498,13 +458,10 @@ class Raster3DMetadata(RasterMetadataBase):
         #      0123456789012345678901234567890
         #      0123456789012345678901234567890
         print " | Number of depths:........... " + str(self.get_depths())
         print " | Number of depths:........... " + str(self.get_depths())
         print " | Top-Bottom resolution:...... " + str(self.get_tbres())
         print " | Top-Bottom resolution:...... " + str(self.get_tbres())
-        print " | STR3DS register table ...... " + str(
-                                                self.get_str3ds_register())
 
 
     def print_shell_info(self):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!Print information about this class in shell style"""
         RasterMetadataBase.print_shell_info(self)
         RasterMetadataBase.print_shell_info(self)
-        print "str3ds_register=" + str(self.get_str3ds_register())
         print "depths=" + str(self.get_depths())
         print "depths=" + str(self.get_depths())
         print "tbres=" + str(self.get_tbres())
         print "tbres=" + str(self.get_tbres())
 
 
@@ -558,7 +515,6 @@ class VectorMetadata(SQLDatabaseInterface):
         12
         12
         >>> meta.print_info()
         >>> meta.print_info()
          +-------------------- Metadata information ----------------------------------+
          +-------------------- Metadata information ----------------------------------+
-         | STVDS register table ....... None
          | Is map 3d .................. True
          | Is map 3d .................. True
          | Number of points ........... 1
          | Number of points ........... 1
          | Number of lines ............ 2
          | Number of lines ............ 2
@@ -573,7 +529,6 @@ class VectorMetadata(SQLDatabaseInterface):
          | Number of holes ............ 11
          | Number of holes ............ 11
          | Number of volumes .......... 12
          | Number of volumes .......... 12
         >>> meta.print_shell_info()
         >>> meta.print_shell_info()
-        stvds_register=None
         is_3d=True
         is_3d=True
         points=1
         points=1
         lines=2
         lines=2
@@ -591,7 +546,7 @@ class VectorMetadata(SQLDatabaseInterface):
         @endcode
         @endcode
     """
     """
     def __init__(
     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_points=None, number_of_lines=None, number_of_boundaries=None,
         number_of_centroids=None, number_of_faces=None, number_of_kernels=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,
         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)
         SQLDatabaseInterface.__init__(self, "vector_metadata", ident)
 
 
         self.set_id(ident)
         self.set_id(ident)
-        self.set_stvds_register(stvds_register)
         self.set_3d_info(is_3d)
         self.set_3d_info(is_3d)
         self.set_number_of_points(number_of_points)
         self.set_number_of_points(number_of_points)
         self.set_number_of_lines(number_of_lines)
         self.set_number_of_lines(number_of_lines)
@@ -620,10 +574,6 @@ class VectorMetadata(SQLDatabaseInterface):
         self.ident = ident
         self.ident = ident
         self.D["id"] = 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):
     def set_3d_info(self, is_3d):
         """!Set True if the vector map is three dimensional"""
         """!Set True if the vector map is three dimensional"""
         self.D["is_3d"] = is_3d
         self.D["is_3d"] = is_3d
@@ -685,14 +635,6 @@ class VectorMetadata(SQLDatabaseInterface):
         else:
         else:
             return None
             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):
     def get_3d_info(self):
         """!Return True if the map is three dimensional,
         """!Return True if the map is three dimensional,
            False if not and None if not info was found"""
            False if not and None if not info was found"""
@@ -799,8 +741,6 @@ class VectorMetadata(SQLDatabaseInterface):
 
 
     # Set the properties
     # Set the properties
     id  = property(fget=get_id, fset=set_id)
     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)
     is_3d  = property(fget=get_3d_info, fset=set_3d_info)
     number_of_points = property(fget=get_number_of_points,
     number_of_points = property(fget=get_number_of_points,
                                 fset=set_number_of_points)
                                 fset=set_number_of_points)
@@ -831,8 +771,6 @@ class VectorMetadata(SQLDatabaseInterface):
         """!Print information about this class in human readable style"""
         """!Print information about this class in human readable style"""
         #      0123456789012345678901234567890
         #      0123456789012345678901234567890
         print " +-------------------- Metadata information ----------------------------------+"
         print " +-------------------- Metadata information ----------------------------------+"
-        print " | STVDS register table ....... " + str(
-            self.get_stvds_register())
         print " | Is map 3d .................. " + str(self.get_3d_info())
         print " | Is map 3d .................. " + str(self.get_3d_info())
         print " | Number of points ........... " + str(self.get_number_of_points())
         print " | Number of points ........... " + str(self.get_number_of_points())
         print " | Number of lines ............ " + str(self.get_number_of_lines())
         print " | Number of lines ............ " + str(self.get_number_of_lines())
@@ -849,7 +787,6 @@ class VectorMetadata(SQLDatabaseInterface):
 
 
     def print_shell_info(self):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!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 "is_3d=" + str(self.get_3d_info())
         print "points=" + str(self.get_number_of_points())
         print "points=" + str(self.get_number_of_points())
         print "lines=" + str(self.get_number_of_lines())
         print "lines=" + str(self.get_number_of_lines())
@@ -1068,6 +1005,7 @@ class STDSRasterMetadataBase(STDSMetadataBase):
          | Minimum value max:.......... None
          | Minimum value max:.......... None
          | Maximum value min:.......... None
          | Maximum value min:.......... None
          | Maximum value max:.......... None
          | Maximum value max:.......... None
+         | Aggregation type:........... None
          | Number of registered maps:.. None
          | Number of registered maps:.. None
          |
          |
          | Title:
          | Title:
@@ -1076,6 +1014,7 @@ class STDSRasterMetadataBase(STDSMetadataBase):
          | Soils 1950 - 2010
          | Soils 1950 - 2010
          | Command history:
          | Command history:
         >>> meta.print_shell_info()
         >>> meta.print_shell_info()
+        aggregation_type=None
         number_of_maps=None
         number_of_maps=None
         nsres_min=None
         nsres_min=None
         nsres_max=None
         nsres_max=None
@@ -1088,7 +1027,7 @@ class STDSRasterMetadataBase(STDSMetadataBase):
 
 
         @endcode
         @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)
         STDSMetadataBase.__init__(self, table, ident, title, description)
 
 
@@ -1101,7 +1040,21 @@ class STDSRasterMetadataBase(STDSMetadataBase):
         self.D["nsres_max"] = None
         self.D["nsres_max"] = None
         self.D["ewres_min"] = None
         self.D["ewres_min"] = None
         self.D["ewres_max"] = 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):
     def get_max_min(self):
         """!Get the minimal maximum of all registered maps,
         """!Get the minimal maximum of all registered maps,
            this value is set in the database
            this value is set in the database
@@ -1190,6 +1143,8 @@ class STDSRasterMetadataBase(STDSMetadataBase):
     min_max = property(fget=get_min_max)
     min_max = property(fget=get_min_max)
     max_min = property(fget=get_max_min)
     max_min = property(fget=get_max_min)
     max_max = property(fget=get_max_max)
     max_max = property(fget=get_max_max)
+    aggregation_type = property(fset=set_aggregation_type, 
+                                fget=get_aggregation_type)
 
 
     def print_info(self):
     def print_info(self):
         """!Print information about this class in human readable style"""
         """!Print information about this class in human readable style"""
@@ -1202,10 +1157,12 @@ class STDSRasterMetadataBase(STDSMetadataBase):
         print " | Minimum value max:.......... " + str(self.get_min_max())
         print " | Minimum value max:.......... " + str(self.get_min_max())
         print " | Maximum value min:.......... " + str(self.get_max_min())
         print " | Maximum value min:.......... " + str(self.get_max_min())
         print " | Maximum value max:.......... " + str(self.get_max_max())
         print " | Maximum value max:.......... " + str(self.get_max_max())
+        print " | Aggregation type:........... " + str(self.get_aggregation_type())
         STDSMetadataBase.print_info(self)
         STDSMetadataBase.print_info(self)
 
 
     def print_shell_info(self):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!Print information about this class in shell style"""
+        print "aggregation_type=" + str(self.get_aggregation_type())
         STDSMetadataBase.print_shell_info(self)
         STDSMetadataBase.print_shell_info(self)
         print "nsres_min=" + str(self.get_nsres_min())
         print "nsres_min=" + str(self.get_nsres_min())
         print "nsres_max=" + str(self.get_nsres_max())
         print "nsres_max=" + str(self.get_nsres_max())
@@ -1264,6 +1221,7 @@ class STRDSMetadata(STDSRasterMetadataBase):
          | Minimum value max:.......... None
          | Minimum value max:.......... None
          | Maximum value min:.......... None
          | Maximum value min:.......... None
          | Maximum value max:.......... None
          | Maximum value max:.......... None
+         | Aggregation type:........... None
          | Number of registered maps:.. None
          | Number of registered maps:.. None
          |
          |
          | Title:
          | Title:
@@ -1272,6 +1230,7 @@ class STRDSMetadata(STDSRasterMetadataBase):
          | Soils 1950 - 2010
          | Soils 1950 - 2010
          | Command history:
          | Command history:
         >>> meta.print_shell_info()
         >>> meta.print_shell_info()
+        aggregation_type=None
         number_of_maps=None
         number_of_maps=None
         nsres_min=None
         nsres_min=None
         nsres_max=None
         nsres_max=None
@@ -1372,6 +1331,7 @@ class STR3DSMetadata(STDSRasterMetadataBase):
          | Minimum value max:.......... None
          | Minimum value max:.......... None
          | Maximum value min:.......... None
          | Maximum value min:.......... None
          | Maximum value max:.......... None
          | Maximum value max:.......... None
+         | Aggregation type:........... None
          | Number of registered maps:.. None
          | Number of registered maps:.. None
          |
          |
          | Title:
          | Title:
@@ -1380,6 +1340,7 @@ class STR3DSMetadata(STDSRasterMetadataBase):
          | Soils 1950 - 2010
          | Soils 1950 - 2010
          | Command history:
          | Command history:
         >>> meta.print_shell_info()
         >>> meta.print_shell_info()
+        aggregation_type=None
         number_of_maps=None
         number_of_maps=None
         nsres_min=None
         nsres_min=None
         nsres_max=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
             # Save the datasets that must be updated
             datasets = map.get_registered_datasets(dbif)
             datasets = map.get_registered_datasets(dbif)
-            if datasets:
+            if datasets is not None:
                 for dataset in datasets:
                 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():
                 if name and map.get_temporal_type() != sp.get_temporal_type():
                     dbif.close()
                     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") %
             msgr.debug(1, _("Set absolute valid time for map <%s> to %s - %s") %
                          (map.get_map_id(), str(start_time), str(end_time)))
                          (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:
     else:
         start_time = int(start)
         start_time = int(start)
         end_time = None
         end_time = None

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

@@ -73,7 +73,6 @@ class RasterDataset(AbstractMapDataset):
          | East-west resolution:....... 10.0
          | East-west resolution:....... 10.0
          | Minimum value:.............. 1.0
          | Minimum value:.............. 1.0
          | Maximum value:.............. 1.0
          | Maximum value:.............. 1.0
-         | STRDS register table ....... None
 
 
         >>> newmap = rmap.get_new_instance("new@PERMANENT")
         >>> newmap = rmap.get_new_instance("new@PERMANENT")
         >>> isinstance(newmap, RasterDataset)
         >>> isinstance(newmap, RasterDataset)
@@ -83,12 +82,11 @@ class RasterDataset(AbstractMapDataset):
         True
         True
         >>> rmap.get_type()
         >>> rmap.get_type()
         'raster'
         'raster'
-        >>> rmap.get_stds_register()
         >>> rmap.set_absolute_time(start_time=datetime(2001,1,1),
         >>> rmap.set_absolute_time(start_time=datetime(2001,1,1),
         ...                        end_time=datetime(2012,1,1))
         ...                        end_time=datetime(2012,1,1))
         True
         True
         >>> rmap.get_absolute_time()
         >>> 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()
         >>> rmap.get_temporal_extent_as_tuple()
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> rmap.get_name()
         >>> rmap.get_name()
@@ -114,6 +112,13 @@ class RasterDataset(AbstractMapDataset):
         AbstractMapDataset.__init__(self)
         AbstractMapDataset.__init__(self)
         self.reset(ident)
         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):
     def get_type(self):
         return 'raster'
         return 'raster'
 
 
@@ -126,16 +131,6 @@ class RasterDataset(AbstractMapDataset):
         are stored with the type of this class"""
         are stored with the type of this class"""
         return SpaceTimeRasterDataset(ident)
         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):
     def spatial_overlapping(self, dataset):
         """!Return True if the spatial extents 2d overlap"""
         """!Return True if the spatial extents 2d overlap"""
         return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
         return self.spatial_extent.overlapping_2d(dataset.spatial_extent)
@@ -199,6 +194,7 @@ class RasterDataset(AbstractMapDataset):
         self.relative_time = RasterRelativeTime(ident=ident)
         self.relative_time = RasterRelativeTime(ident=ident)
         self.spatial_extent = RasterSpatialExtent(ident=ident)
         self.spatial_extent = RasterSpatialExtent(ident=ident)
         self.metadata = RasterMetadata(ident=ident)
         self.metadata = RasterMetadata(ident=ident)
+        self.stds_register = RasterSTDSRegister(ident=ident)
 
 
     def has_grass_timestamp(self):
     def has_grass_timestamp(self):
         """!Check if a grass file bsased time stamp exists for this map.
         """!Check if a grass file bsased time stamp exists for this map.
@@ -229,7 +225,7 @@ class RasterDataset(AbstractMapDataset):
             return False
             return False
 
 
         if len(dates) == 2:
         if len(dates) == 2:
-            self.set_absolute_time(dates[0], dates[1], None)
+            self.set_absolute_time(dates[0], dates[1])
         else:
         else:
             self.set_relative_time(dates[0], dates[1], dates[2])
             self.set_relative_time(dates[0], dates[1], dates[2])
 
 
@@ -380,7 +376,6 @@ class Raster3DDataset(AbstractMapDataset):
          | Maximum value:.............. 1.0
          | Maximum value:.............. 1.0
          | Number of depths:........... 10
          | Number of depths:........... 10
          | Top-Bottom resolution:...... 10.0
          | Top-Bottom resolution:...... 10.0
-         | STR3DS register table ...... None
 
 
         >>> newmap = r3map.get_new_instance("new@PERMANENT")
         >>> newmap = r3map.get_new_instance("new@PERMANENT")
         >>> isinstance(newmap, Raster3DDataset)
         >>> isinstance(newmap, Raster3DDataset)
@@ -390,12 +385,11 @@ class Raster3DDataset(AbstractMapDataset):
         True
         True
         >>> r3map.get_type()
         >>> r3map.get_type()
         'raster3d'
         'raster3d'
-        >>> r3map.get_stds_register()
         >>> r3map.set_absolute_time(start_time=datetime(2001,1,1),
         >>> r3map.set_absolute_time(start_time=datetime(2001,1,1),
         ...                        end_time=datetime(2012,1,1))
         ...                        end_time=datetime(2012,1,1))
         True
         True
         >>> r3map.get_absolute_time()
         >>> 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()
         >>> r3map.get_temporal_extent_as_tuple()
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> r3map.get_name()
         >>> r3map.get_name()
@@ -418,6 +412,13 @@ class Raster3DDataset(AbstractMapDataset):
         AbstractMapDataset.__init__(self)
         AbstractMapDataset.__init__(self)
         self.reset(ident)
         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):
     def get_type(self):
         return "raster3d"
         return "raster3d"
 
 
@@ -430,16 +431,6 @@ class Raster3DDataset(AbstractMapDataset):
         are stored with the type of this class"""
         are stored with the type of this class"""
         return SpaceTimeRaster3DDataset(ident)
         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):
     def spatial_overlapping(self, dataset):
         """!Return True if the spatial extents overlap"""
         """!Return True if the spatial extents overlap"""
         if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds":
         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.relative_time = Raster3DRelativeTime(ident=ident)
         self.spatial_extent = Raster3DSpatialExtent(ident=ident)
         self.spatial_extent = Raster3DSpatialExtent(ident=ident)
         self.metadata = Raster3DMetadata(ident=ident)
         self.metadata = Raster3DMetadata(ident=ident)
+        self.stds_register = Raster3DSTDSRegister(ident=ident)
 
 
     def has_grass_timestamp(self):
     def has_grass_timestamp(self):
         """!Check if a grass file bsased time stamp exists for this map.
         """!Check if a grass file bsased time stamp exists for this map.
@@ -548,7 +540,7 @@ class Raster3DDataset(AbstractMapDataset):
             return False
             return False
 
 
         if len(dates) == 2:
         if len(dates) == 2:
-            self.set_absolute_time(dates[0], dates[1], None)
+            self.set_absolute_time(dates[0], dates[1])
         else:
         else:
             self.set_relative_time(dates[0], dates[1], dates[2])
             self.set_relative_time(dates[0], dates[1], dates[2])
 
 
@@ -679,7 +671,6 @@ class VectorDataset(AbstractMapDataset):
          | End time:................... None
          | End time:................... None
         >>> vmap.metadata.print_info()
         >>> vmap.metadata.print_info()
          +-------------------- Metadata information ----------------------------------+
          +-------------------- Metadata information ----------------------------------+
-         | STVDS register table ....... None
          | Is map 3d .................. True
          | Is map 3d .................. True
          | Number of points ........... 100
          | Number of points ........... 100
          | Number of lines ............ 0
          | Number of lines ............ 0
@@ -701,12 +692,11 @@ class VectorDataset(AbstractMapDataset):
         True
         True
         >>> vmap.get_type()
         >>> vmap.get_type()
         'vector'
         'vector'
-        >>> vmap.get_stds_register()
         >>> vmap.set_absolute_time(start_time=datetime(2001,1,1),
         >>> vmap.set_absolute_time(start_time=datetime(2001,1,1),
         ...                        end_time=datetime(2012,1,1))
         ...                        end_time=datetime(2012,1,1))
         True
         True
         >>> vmap.get_absolute_time()
         >>> 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()
         >>> vmap.get_temporal_extent_as_tuple()
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0))
         >>> vmap.get_name()
         >>> vmap.get_name()
@@ -729,6 +719,13 @@ class VectorDataset(AbstractMapDataset):
         AbstractMapDataset.__init__(self)
         AbstractMapDataset.__init__(self)
         self.reset(ident)
         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):
     def get_type(self):
         return "vector"
         return "vector"
 
 
@@ -741,16 +738,6 @@ class VectorDataset(AbstractMapDataset):
         are stored with the type of this class"""
         are stored with the type of this class"""
         return SpaceTimeVectorDataset(ident)
         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):
     def get_layer(self):
         """!Return the layer"""
         """!Return the layer"""
         return self.base.get_layer()
         return self.base.get_layer()
@@ -798,6 +785,7 @@ class VectorDataset(AbstractMapDataset):
         self.relative_time = VectorRelativeTime(ident=ident)
         self.relative_time = VectorRelativeTime(ident=ident)
         self.spatial_extent = VectorSpatialExtent(ident=ident)
         self.spatial_extent = VectorSpatialExtent(ident=ident)
         self.metadata = VectorMetadata(ident=ident)
         self.metadata = VectorMetadata(ident=ident)
+        self.stds_register = VectorSTDSRegister(ident=ident)
 
 
     def has_grass_timestamp(self):
     def has_grass_timestamp(self):
         """!Check if a grass file bsased time stamp exists for this map.
         """!Check if a grass file bsased time stamp exists for this map.
@@ -826,7 +814,7 @@ class VectorDataset(AbstractMapDataset):
             return False
             return False
 
 
         if len(dates) == 2:
         if len(dates) == 2:
-            self.set_absolute_time(dates[0], dates[1], None)
+            self.set_absolute_time(dates[0], dates[1])
         else:
         else:
             self.set_relative_time(dates[0], dates[1], dates[2])
             self.set_relative_time(dates[0], dates[1], dates[2])
 
 
@@ -920,6 +908,13 @@ class SpaceTimeRasterDataset(AbstractSpaceTimeDataset):
     def __init__(self, ident):
     def __init__(self, ident):
         AbstractSpaceTimeDataset.__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):
     def get_type(self):
         return "strds"
         return "strds"
 
 
@@ -993,6 +988,13 @@ class SpaceTimeRaster3DDataset(AbstractSpaceTimeDataset):
     def __init__(self, ident):
     def __init__(self, ident):
         AbstractSpaceTimeDataset.__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):
     def get_type(self):
         return "str3ds"
         return "str3ds"
 
 
@@ -1085,6 +1087,13 @@ class SpaceTimeVectorDataset(AbstractSpaceTimeDataset):
     def __init__(self, ident):
     def __init__(self, ident):
         AbstractSpaceTimeDataset.__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):
     def get_type(self):
         return "stvds"
         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,
             return RelativeTemporalExtent(start_time=start, end_time=end,
                                           unit=self.get_unit())
                                           unit=self.get_unit())
         elif issubclass(type(self), AbsoluteTemporalExtent):
         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):
         elif issubclass(type(self), AbstractTemporalExtent):
             return AbstractTemporalExtent(start_time=start, end_time=end)
             return AbstractTemporalExtent(start_time=start, end_time=end)
 
 
@@ -384,8 +383,7 @@ class AbstractTemporalExtent(SQLDatabaseInterface):
             return RelativeTemporalExtent(start_time=start, end_time=end,
             return RelativeTemporalExtent(start_time=start, end_time=end,
                                           unit=self.get_unit())
                                           unit=self.get_unit())
         elif issubclass(type(self), AbsoluteTemporalExtent):
         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):
         elif issubclass(type(self), AbstractTemporalExtent):
             return AbstractTemporalExtent(start_time=start, end_time=end)
             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
         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__(
         AbstractTemporalExtent.__init__(
             self, table, ident, start_time, end_time)
             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):
     def print_info(self):
         """!Print information about this class in human readable style"""
         """!Print information about this class in human readable style"""
         #      0123456789012345678901234567890
         #      0123456789012345678901234567890
         print " +-------------------- Absolute time -----------------------------------------+"
         print " +-------------------- Absolute time -----------------------------------------+"
         AbstractTemporalExtent.print_info(self)
         AbstractTemporalExtent.print_info(self)
-        #print " | Timezone:................... " + str(self.get_timezone())
 
 
     def print_shell_info(self):
     def print_shell_info(self):
         """!Print information about this class in shell style"""
         """!Print information about this class in shell style"""
         AbstractTemporalExtent.print_shell_info(self)
         AbstractTemporalExtent.print_shell_info(self)
-        #print "timezone=" + str(self.get_timezone())
 
 
 ###############################################################################
 ###############################################################################
 
 
 class RasterAbsoluteTime(AbsoluteTemporalExtent):
 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",
         AbsoluteTemporalExtent.__init__(self, "raster_absolute_time",
-            ident, start_time, end_time, timezone)
+            ident, start_time, end_time)
 
 
 class Raster3DAbsoluteTime(AbsoluteTemporalExtent):
 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",
         AbsoluteTemporalExtent.__init__(self, "raster3d_absolute_time",
-            ident, start_time, end_time, timezone)
+            ident, start_time, end_time)
 
 
 class VectorAbsoluteTime(AbsoluteTemporalExtent):
 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",
         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
         @endcode
     """
     """
     def __init__(self, table=None, ident=None, start_time=None, end_time=None,
     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__(
         AbsoluteTemporalExtent.__init__(
-            self, table, ident, start_time, end_time, timezone)
+            self, table, ident, start_time, end_time)
 
 
         self.set_granularity(granularity)
         self.set_granularity(granularity)
         self.set_map_time(map_time)
         self.set_map_time(map_time)
@@ -1221,23 +1192,23 @@ class STDSAbsoluteTime(AbsoluteTemporalExtent):
 
 
 class STRDSAbsoluteTime(STDSAbsoluteTime):
 class STRDSAbsoluteTime(STDSAbsoluteTime):
     def __init__(self, ident=None, start_time=None, end_time=None,
     def __init__(self, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None):
+                 granularity=None):
         STDSAbsoluteTime.__init__(self, "strds_absolute_time",
         STDSAbsoluteTime.__init__(self, "strds_absolute_time",
-            ident, start_time, end_time, granularity, timezone)
+            ident, start_time, end_time, granularity)
 
 
 
 
 class STR3DSAbsoluteTime(STDSAbsoluteTime):
 class STR3DSAbsoluteTime(STDSAbsoluteTime):
     def __init__(self, ident=None, start_time=None, end_time=None,
     def __init__(self, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None):
+                 granularity=None):
         STDSAbsoluteTime.__init__(self, "str3ds_absolute_time",
         STDSAbsoluteTime.__init__(self, "str3ds_absolute_time",
-            ident, start_time, end_time, granularity, timezone)
+            ident, start_time, end_time, granularity)
 
 
 
 
 class STVDSAbsoluteTime(STDSAbsoluteTime):
 class STVDSAbsoluteTime(STDSAbsoluteTime):
     def __init__(self, ident=None, start_time=None, end_time=None,
     def __init__(self, ident=None, start_time=None, end_time=None,
-                 granularity=None, timezone=None):
+                 granularity=None):
         STDSAbsoluteTime.__init__(self, "stvds_absolute_time",
         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__(
         AbstractTemporalExtent.__init__(
             self, table, ident, start_time, end_time)
             self, table, ident, start_time, end_time)
-        self.D["unit"] = unit
+        self.set_unit(unit)
 
 
     def set_unit(self, unit):
     def set_unit(self, unit):
         """!Set the unit of the relative time. Valid units are:
         """!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):
     def setUpClass(cls):
         """!Initiate the temporal GIS and set the region
         """!Initiate the temporal GIS and set the region
         """
         """
-        tgis.init(True)
+        tgis.init()
         grass.overwrite = True
         grass.overwrite = True
         grass.use_temp_region()
         grass.use_temp_region()
         ret = grass.run_command("g.region", n=80.0, s=0.0, e=120.0, 
         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 = tgis.RasterDataset("register_map_1@" + tgis.get_current_mapset())
         map.select()
         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(start, datetime.datetime(2001, 1, 1))
         self.assertEqual(end, datetime.datetime(2001, 1, 2))
         self.assertEqual(end, datetime.datetime(2001, 1, 2))
 
 
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map.select()
         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(start, datetime.datetime(2001, 1, 2))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
 
 
         strds = tgis.SpaceTimeRasterDataset("register_test@" + tgis.get_current_mapset())
         strds = tgis.SpaceTimeRasterDataset("register_test@" + tgis.get_current_mapset())
         strds.select()
         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(start, datetime.datetime(2001, 1, 1))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
         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 = tgis.RasterDataset("register_map_1@" + tgis.get_current_mapset())
         map.select()
         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(start, datetime.datetime(2001, 1, 1))
         self.assertEqual(end, datetime.datetime(2001, 1, 2))
         self.assertEqual(end, datetime.datetime(2001, 1, 2))
 
 
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map.select()
         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(start, datetime.datetime(2001, 1, 2))
         self.assertEqual(end, datetime.datetime(2001, 1, 3))
         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 = tgis.RasterDataset("register_map_1@" + tgis.get_current_mapset())
         map.select()
         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))
         self.assertEqual(start, datetime.datetime(2001, 1, 1, 10, 30, 1))
 
 
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map = tgis.RasterDataset("register_map_2@" + tgis.get_current_mapset())
         map.select()
         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))
         self.assertEqual(start, datetime.datetime(2001, 1, 1, 18, 30, 1))
 
 
     def test_relative_time_strds(self):
     def test_relative_time_strds(self):
@@ -140,6 +140,8 @@ class TestRegisterFunctions(unittest.TestCase):
         self.assertEqual(start, 0)
         self.assertEqual(start, 0)
         self.assertEqual(end, 2)
         self.assertEqual(end, 2)
         self.assertEqual(unit, "day")
         self.assertEqual(unit, "day")
+        
+        strds.print_info()
 
 
         ret = grass.run_command("t.remove", input="register_test") 
         ret = grass.run_command("t.remove", input="register_test") 
         self.assertEqual(ret, 0)
         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
 -- GRASS_MAP is a placeholder for specific map type: raster, raster3d or vector
 
 
---PRAGMA foreign_keys = ON;
 
 
 CREATE TABLE  GRASS_MAP_base (
 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
   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,
   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
   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
   creation_time TIMESTAMP NOT NULL,      -- The time of creation of the grass map
   PRIMARY KEY (id)
   PRIMARY KEY (id)
 );
 );
 
 
-CREATE INDEX GRASS_MAP_base_index ON GRASS_MAP_base (id);
-
 -- Relative valid time interval with start and end time
 -- Relative valid time interval with start and end time
 CREATE TABLE  GRASS_MAP_relative_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 
   start_time INTEGER,  -- The relative valid start time in 
   end_time INTEGER,    -- The relative valid end time in 
   end_time INTEGER,    -- The relative valid end time in 
   unit VARCHAR,                 -- The relative time unit, available are "years, months, days, minutes, seconds"
   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 (
 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
   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
   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
 -- The spatial extent of a raster map
 
 
 CREATE TABLE  GRASS_MAP_spatial_extent (
 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
   -- below is the spatial extent of the map
   north DOUBLE PRECISION NOT NULL,
   north DOUBLE PRECISION NOT NULL,
   south DOUBLE PRECISION NOT NULL,
   south DOUBLE PRECISION NOT NULL,
@@ -63,7 +52,13 @@ CREATE TABLE  GRASS_MAP_spatial_extent (
   top DOUBLE PRECISION NOT NULL,
   top DOUBLE PRECISION NOT NULL,
   bottom DOUBLE PRECISION NOT NULL,
   bottom DOUBLE PRECISION NOT NULL,
   proj VARCHAR,
   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
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 --#############################################################################
 
 
---PRAGMA foreign_keys = ON;
 
 
 -- The metadata table reflects most of the raster3d metadata available in grass
 -- The metadata table reflects most of the raster3d metadata available in grass
 
 
 CREATE TABLE  raster3d_metadata (
 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,
   datatype VARCHAR NOT NULL,
   cols INTEGER NOT NULL,
   cols INTEGER NOT NULL,
   rows INTEGER NOT NULL,
   rows INTEGER NOT NULL,
@@ -22,8 +20,9 @@ CREATE TABLE  raster3d_metadata (
   tbres DOUBLE PRECISION NOT NULL,
   tbres DOUBLE PRECISION NOT NULL,
   min DOUBLE PRECISION,
   min DOUBLE PRECISION,
   max 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 
 CREATE VIEW raster3d_view_abs_time AS SELECT 
             A1.id, A1.mapset,
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.name, A1.temporal_type,
-            A1.creation_time, 
+            A1.creation_time,  
             A1.creator,
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows, A4.depths,
             A4.datatype, A4.cols, A4.rows, A4.depths,
             A4.nsres, A4.ewres, A4.tbres,
             A4.nsres, A4.ewres, A4.tbres,
             A4.min, A4.max,
             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, 
             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 
 CREATE VIEW raster3d_view_rel_time AS SELECT 
             A1.id, A1.mapset,
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.name, A1.temporal_type,
             A1.creation_time, 
             A1.creation_time, 
             A1.creator,
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows, A4.depths,
             A4.datatype, A4.cols, A4.rows, A4.depths,
             A4.nsres, A4.ewres, A4.tbres,
             A4.nsres, A4.ewres, A4.tbres,
             A4.min, A4.max,
             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, 
             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
 -- The metadata table reflects most of the raster metadata available in grass
 
 
 CREATE TABLE  raster_metadata (
 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,
   datatype VARCHAR NOT NULL,
   cols INTEGER NOT NULL,
   cols INTEGER NOT NULL,
   rows INTEGER NOT NULL,
   rows INTEGER NOT NULL,
@@ -20,7 +19,7 @@ CREATE TABLE  raster_metadata (
   ewres DOUBLE PRECISION NOT NULL,
   ewres DOUBLE PRECISION NOT NULL,
   min DOUBLE PRECISION,
   min DOUBLE PRECISION,
   max 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.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.name, A1.temporal_type,
             A1.creation_time, 
             A1.creation_time, 
             A1.creator, 
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows,
             A4.datatype, A4.cols, A4.rows,
             A4.nsres, A4.ewres, A4.min, A4.max,
             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, 
             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 
 CREATE VIEW raster_view_rel_time AS SELECT 
             A1.id, A1.mapset,
             A1.id, A1.mapset,
             A1.name, A1.temporal_type,
             A1.name, A1.temporal_type,
             A1.creation_time, 
             A1.creation_time, 
             A1.creator, 
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.datatype, A4.cols, A4.rows,
             A4.datatype, A4.cols, A4.rows,
             A4.nsres, A4.ewres, A4.min, A4.max,
             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, 
             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
 CREATE TRIGGER delete_strds_base AFTER DELETE ON strds_base
   BEGIN
   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_relative_time WHERE id = old.id;
     DELETE FROM raster_spatial_extent WHERE id = old.id;
     DELETE FROM raster_spatial_extent WHERE id = old.id;
     DELETE FROM raster_metadata WHERE id = old.id;
     DELETE FROM raster_metadata WHERE id = old.id;
+    DELETE FROM raster_stds_register WHERE id = old.id;
   END;
   END;
 
 
 CREATE TRIGGER delete_str3ds_base AFTER DELETE ON str3ds_base
 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_relative_time WHERE id = old.id;
     DELETE FROM raster3d_spatial_extent WHERE id = old.id;
     DELETE FROM raster3d_spatial_extent WHERE id = old.id;
     DELETE FROM raster3d_metadata WHERE id = old.id;
     DELETE FROM raster3d_metadata WHERE id = old.id;
+    DELETE FROM raster3d_stds_register WHERE id = old.id;
   END;
   END;
 
 
 CREATE TRIGGER delete_stvds_base AFTER DELETE ON stvds_base
 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_relative_time WHERE id = old.id;
     DELETE FROM vector_spatial_extent WHERE id = old.id;
     DELETE FROM vector_spatial_extent WHERE id = old.id;
     DELETE FROM vector_metadata WHERE id = old.id;
     DELETE FROM vector_metadata WHERE id = old.id;
+    DELETE FROM vector_stds_register WHERE id = old.id;
   END;
   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
 -- 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
 -- 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
 -- 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 (
 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
 -- STDS is a placeholder for specific space-time dataset type: strds, str3ds, stvds
 
 
---PRAGMA foreign_keys = ON;
-
 CREATE TABLE  STDS_base (
 CREATE TABLE  STDS_base (
   id VARCHAR NOT NULL,                 -- Id of the space-time dataset, name@mapset this is the primary key
   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
   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" 
   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
   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
   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)
   PRIMARY KEY (id)
 );
 );
 
 
 CREATE TABLE  STDS_relative_time (
 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 
   start_time INTEGER,             -- The relative valid start time 
   end_time INTEGER,               -- The relative valid end time 
   end_time INTEGER,               -- The relative valid end time 
   granularity INTEGER,            -- The granularity 
   granularity INTEGER,            -- The granularity 
   unit VARCHAR,                   -- The relative time unit, available are "years, months, days, minutes, seconds"
   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
   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 (
 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
   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
   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"
   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
   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 (
 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
   north DOUBLE PRECISION,   -- The spatial north extent, derived from the registered maps
   south DOUBLE PRECISION,   -- The spatial south 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
   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
   top DOUBLE PRECISION,     -- The spatial top extent, derived from the registered maps
   bottom DOUBLE PRECISION,  -- The spatial bottom 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)
   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
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 --#############################################################################
 
 
---PRAGMA foreign_keys = ON;
-
 CREATE TABLE  str3ds_metadata (
 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
   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
   number_of_maps INTEGER,       -- The number of registered 3D raster maps
   max_min DOUBLE PRECISION,     -- The minimal maximum of the 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
   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_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
   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
   title VARCHAR,                -- Title of the space time 3D raster dataset
   description VARCHAR,          -- Detailed description 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
   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 
 CREATE VIEW str3ds_view_abs_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster3d_register,
             A4.raster3d_register,
             A4.number_of_maps, 
             A4.number_of_maps, 
@@ -21,7 +19,7 @@ CREATE VIEW str3ds_view_abs_time AS SELECT
             A4.nsres_max, A4.ewres_max, 
             A4.nsres_max, A4.ewres_max, 
             A4.tbres_min, A4.tbres_max, 
             A4.tbres_min, A4.tbres_max, 
             A4.min_min, A4.min_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
             A4.title, A4.description, A4.command
             FROM str3ds_base A1, str3ds_absolute_time A2,  
             FROM str3ds_base A1, str3ds_absolute_time A2,  
             str3ds_spatial_extent A3, str3ds_metadata A4 WHERE A1.id = A2.id AND 
             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 
 CREATE VIEW str3ds_view_rel_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster3d_register,
             A4.raster3d_register,
             A4.number_of_maps, 
             A4.number_of_maps, 
@@ -42,8 +38,9 @@ CREATE VIEW str3ds_view_rel_time AS SELECT
             A4.nsres_max, A4.ewres_max, 
             A4.nsres_max, A4.ewres_max, 
             A4.tbres_min, A4.tbres_max, 
             A4.tbres_min, A4.tbres_max, 
             A4.min_min, A4.min_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
             A4.title, A4.description, A4.command
             FROM str3ds_base A1, str3ds_relative_time A2,  
             FROM str3ds_base A1, str3ds_relative_time A2,  
             str3ds_spatial_extent A3, str3ds_metadata A4 WHERE A1.id = A2.id AND 
             str3ds_spatial_extent A3, str3ds_metadata A4 WHERE A1.id = A2.id AND 
             A1.id = A3.id AND A1.id = A4.id;
             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
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 --#############################################################################
 
 
---PRAGMA foreign_keys = ON;
 
 
 CREATE TABLE  strds_metadata (
 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
   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
   number_of_maps INTEGER,       -- The number of registered raster maps
   max_min DOUBLE PRECISION,     -- The minimal maximum of the 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
   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_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
   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
   title VARCHAR,                -- Title of the space-time raster dataset
   description VARCHAR,          -- Detailed description 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
   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 
 CREATE VIEW strds_view_abs_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster_register,
             A4.raster_register,
             A4.number_of_maps, 
             A4.number_of_maps, 
             A4.nsres_min, A4.ewres_min, 
             A4.nsres_min, A4.ewres_min, 
             A4.nsres_max, A4.ewres_max, 
             A4.nsres_max, A4.ewres_max, 
             A4.min_min, A4.min_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	
             A4.title, A4.description, A4.command	
             FROM strds_base A1, strds_absolute_time A2,  
             FROM strds_base A1, strds_absolute_time A2,  
             strds_spatial_extent A3, strds_metadata A4 WHERE A1.id = A2.id AND 
             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 
 CREATE VIEW strds_view_rel_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.raster_register,
             A4.raster_register,
             A4.number_of_maps, 
             A4.number_of_maps, 
             A4.nsres_min, A4.ewres_min, 
             A4.nsres_min, A4.ewres_min, 
             A4.nsres_max, A4.ewres_max, 
             A4.nsres_max, A4.ewres_max, 
             A4.min_min, A4.min_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
             A4.title, A4.description, A4.command
             FROM strds_base A1, strds_relative_time A2,  
             FROM strds_base A1, strds_relative_time A2,  
             strds_spatial_extent A3, strds_metadata A4 WHERE A1.id = A2.id AND 
             strds_spatial_extent A3, strds_metadata A4 WHERE A1.id = A2.id AND 
             A1.id = A3.id AND A1.id = A4.id;
             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
 -- Author: Soeren Gebbert soerengebbert <at> googlemail <dot> com
 --#############################################################################
 --#############################################################################
 
 
---PRAGMA foreign_keys = ON;
-
 CREATE TABLE  stvds_metadata (
 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
   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
   number_of_maps INTEGER, -- The number of registered vector maps
   title VARCHAR,          -- Title of the space-time vector dataset
   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)
   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)
   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)
   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 
 CREATE VIEW stvds_view_abs_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             A1.semantic_type, 
             A1.semantic_type, 
-            A1.creation_time, 
+            A1.creation_time, A1.modification_time,
             A1.creator, 
             A1.creator, 
-            A2.start_time, A2.end_time, A2.timezone,
+            A2.start_time, A2.end_time,
             A2.granularity,
             A2.granularity,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.vector_register,
             A4.vector_register,
@@ -28,15 +26,13 @@ CREATE VIEW stvds_view_abs_time AS SELECT
 
 
 CREATE VIEW stvds_view_rel_time AS SELECT 
 CREATE VIEW stvds_view_rel_time AS SELECT 
             A1.id, A1.name, A1.mapset, A1.temporal_type,
             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,
             A3.north, A3.south, A3.east, A3.west, A3.bottom, A3.top, A3.proj,
             A4.vector_register,
             A4.vector_register,
-            A4.number_of_maps, 
+            A4.number_of_maps,
             A4.title, A4.description, A4.command, A4.points, A4.lines,
             A4.title, A4.description, A4.command, A4.points, A4.lines,
             A4.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.primitives, A4.nodes, A4.areas, A4.islands,
             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,  
             FROM stvds_base A1, stvds_relative_time A2,  
             stvds_spatial_extent A3, stvds_metadata A4 WHERE A1.id = A2.id AND 
             stvds_spatial_extent A3, stvds_metadata A4 WHERE A1.id = A2.id AND 
             A1.id = A3.id AND A1.id = A4.id;
             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 min(proj) FROM GRASS_MAP_spatial_extent WHERE GRASS_MAP_spatial_extent.id IN 
     		(SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
     		(SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
        ) WHERE id = 'SPACETIME_ID';
        ) 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 max(tbres) FROM raster3d_metadata WHERE raster3d_metadata.id IN 
     		(SELECT id FROM SPACETIME_NAME_raster3d_register)
     		(SELECT id FROM SPACETIME_NAME_raster3d_register)
        ) WHERE id = 'SPACETIME_ID';
        ) WHERE id = 'SPACETIME_ID';
+

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

@@ -10,8 +10,7 @@
 -- The metadata table 
 -- The metadata table 
 
 
 CREATE TABLE  vector_metadata (
 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 
   is_3d BOOLEAN,          -- This is 1 if the vector map is 3d and 0 otherwise 
   points INTEGER,         -- The number of points
   points INTEGER,         -- The number of points
   lines INTEGER,          -- The number of lines
   lines INTEGER,          -- The number of lines
@@ -25,7 +24,8 @@ CREATE TABLE  vector_metadata (
   islands INTEGER,        -- The number of islands (topological information)
   islands INTEGER,        -- The number of islands (topological information)
   holes INTEGER,          -- The number of holes (topological information)
   holes INTEGER,          -- The number of holes (topological information)
   volumes INTEGER,        -- The number of volumes (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.name, A1.layer, A1.temporal_type,
             A1.creation_time, 
             A1.creation_time, 
             A1.creator, 
             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,
             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.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.primitives, A4.nodes, A4.areas, A4.islands,
             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, 
             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 
 CREATE VIEW vector_view_rel_time AS SELECT 
             A1.id, A1.mapset,
             A1.id, A1.mapset,
             A1.name, A1.layer, A1.temporal_type,
             A1.name, A1.layer, A1.temporal_type,
             A1.creation_time,
             A1.creation_time,
             A1.creator, 
             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,
             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.boundaries, A4.centroids, A4.faces, A4.kernels,
             A4.primitives, A4.nodes, A4.areas, A4.islands,
             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, 
             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"
 MAP_LIST="map_list.txt"
 rm ${MAP_LIST}
 rm ${MAP_LIST}
 
 
-NUM_MAPS=50000
+NUM_MAPS=10000
+
+echo "### Generate raster maps"
 
 
 count=1
 count=1
 while [ $count -lt ${NUM_MAPS} ]; do
 while [ $count -lt ${NUM_MAPS} ]; do
@@ -21,18 +23,39 @@ while [ $count -lt ${NUM_MAPS} ]; do
     count=$((count + 1))
     count=$((count + 1))
 done
 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"
 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"
 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"
 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:
             if new_map:
                 # Set the time stamp and write it to the raster map
                 # Set the time stamp and write it to the raster map
                 if sp.is_time_absolute():
                 if sp.is_time_absolute():
-                    new_map.set_absolute_time(start, end, None)
+                    new_map.set_absolute_time(start, end)
                 else:
                 else:
                     new_map.set_relative_time(start,
                     new_map.set_relative_time(start,
                                               end, sp.get_relative_time_unit())
                                               end, sp.get_relative_time_unit())
@@ -134,6 +134,8 @@ def main():
                 new_sp.register_map(new_map, dbif)
                 new_sp.register_map(new_map, dbif)
 
 
     # Update the spatio-temporal extent and the raster metadata table entries
     # 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)
     new_sp.update_from_registered_maps(dbif)
 
 
     dbif.close()
     dbif.close()

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

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

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

@@ -162,17 +162,17 @@ def main():
         id = _map.get_id()
         id = _map.get_id()
         if overwrite_flags[id] == True:
         if overwrite_flags[id] == True:
             if _map.is_time_absolute():
             if _map.is_time_absolute():
-                start, end, tz = _map.get_absolute_time()
+                start, end = _map.get_absolute_time()
                 if _map.is_in_db():
                 if _map.is_in_db():
                     _map.delete(dbif)
                     _map.delete(dbif)
                 _map = sp.get_new_map_instance(id)
                 _map = sp.get_new_map_instance(id)
-                _map.set_absolute_time(start, end, tz)
+                _map.set_absolute_time(start, end)
             else:
             else:
                 start, end, unit = _map.get_relative_time()
                 start, end, unit = _map.get_relative_time()
                 if _map.is_in_db():
                 if _map.is_in_db():
                     _map.delete(dbif)
                     _map.delete(dbif)
                 _map = sp.get_new_map_instance(id)
                 _map = sp.get_new_map_instance(id)
-                _map.set_relative_time(start, end, tz)
+                _map.set_relative_time(start, end, unit)
         _map.load()
         _map.load()
         _map.insert(dbif)
         _map.insert(dbif)
         sp.register_map(_map, 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"
     title="A test with input files" descr="A test with input files"
 
 
 # The first @test
 # 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.bz2 compression=bzip2 format=GTiff workdir=test
 t.rast.export input=precip_rel output=strds_export.tar.gz compression=gzip 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)
                                 method=method)
 
 
         if ret == 0 and not add_time:
         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
             # Create the time range for the output map
             if output.find("@") >= 0:
             if output.find("@") >= 0:
                 id = output
                 id = output
@@ -119,11 +114,7 @@ def main():
 
 
             map = sp.get_new_map_instance(id)
             map = sp.get_new_map_instance(id)
             map.load()
             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
             # Register the map in the temporal database
             if map.is_in_db():
             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.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.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
 # The first @test
 t.rast3d.univar -he input=precip_abs1 
 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,
         type=type, name=name, maps=maps, file=file, start=start, end=end,
         unit=unit, increment=increment, dbif=None, interval=interval, fs=fs)
         unit=unit, increment=increment, dbif=None, interval=interval, fs=fs)
 
 
+
 ###############################################################################
 ###############################################################################
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     options, flags = grass.parser()
     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
 #% key: f
 #% description: Force recursive removing
 #% description: Force recursive removing
 #%end
 #%end
+
 import grass.script as grass
 import grass.script as grass
 import grass.temporal as tgis
 import grass.temporal as tgis
 import grass.pygrass.modules as pyg
 import grass.pygrass.modules as pyg
@@ -154,4 +155,5 @@ def main():
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     options, flags = grass.parser()
     options, flags = grass.parser()
-    main()
+    
+    tgis.profile_function(main)

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

@@ -130,10 +130,10 @@ def main():
             else:
             else:
                 # Delete the map from the temporal database
                 # Delete the map from the temporal database
                 # We need to update all effected space time datasets
                 # 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
                 # Collect the delete statements
                 statement += map.delete(dbif=dbif, update=False, execute=False)
                 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.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
 t.info type=strds input=precip_rel1
 
 
+
 # Check metadata update
 # Check metadata update
 t.info type=strds input=precip_abs1
 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
 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.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
 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
                 # Store all unique dataset ids in a dictionary
                 if datasets:
                 if datasets:
                     for dataset in datasets:
                     for dataset in datasets:
-                        update_dict[dataset["id"]] = dataset["id"]
+                        update_dict[dataset] = dataset
                 # Collect SQL statements
                 # Collect SQL statements
                 statement += map.delete(dbif=dbif, update=False, execute=False)
                 statement += map.delete(dbif=dbif, update=False, execute=False)
         else:
         else:
@@ -170,4 +170,5 @@ def main():
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     options, flags = grass.parser()
     options, flags = grass.parser()
-    main()
+    
+    tgis.profile_function(main)