[GRASS-SVN] r48796 - grass/trunk/lib/python/temporal

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Oct 14 11:22:54 EDT 2011


Author: huhabla
Date: 2011-10-14 08:22:54 -0700 (Fri, 14 Oct 2011)
New Revision: 48796

Modified:
   grass/trunk/lib/python/temporal/abstract_map_dataset.py
   grass/trunk/lib/python/temporal/abstract_space_time_dataset.py
   grass/trunk/lib/python/temporal/datetime_math.py
   grass/trunk/lib/python/temporal/space_time_datasets_tools.py
   grass/trunk/lib/python/temporal/temporal_extent.py
Log:
New dataset object factory. New temporal topology functions implemented.


Modified: grass/trunk/lib/python/temporal/abstract_map_dataset.py
===================================================================
--- grass/trunk/lib/python/temporal/abstract_map_dataset.py	2011-10-14 15:22:13 UTC (rev 48795)
+++ grass/trunk/lib/python/temporal/abstract_map_dataset.py	2011-10-14 15:22:54 UTC (rev 48796)
@@ -56,16 +56,19 @@
            @param timezone: Thee timezone of the map
         
         """
-        if start_time != None and not isinstance(start_time, datetime) :
-            core.fatal(_("Start time must be of type datetime"))
+        if start_time and not isinstance(start_time, datetime) :
+            core.fatal(_("Start time must be of type datetime for %s map <%s>") % (self.get_type(), self.get_id()))
 
-        if end_time != None and not isinstance(end_time, datetime) :
-            core.fatal(_("End time must be of type datetime"))
+        if end_time and not isinstance(end_time, datetime) :
+            core.fatal(_("End time must be of type datetime for %s map <%s>") % (self.get_type(), self.get_id()))
 
-        if start_time != None and end_time != None:
-            if start_time >= end_time:
-                core.error(_("End time must be later than start time"))
-                return False
+        if start_time and end_time:
+            if start_time > end_time:
+                core.fatal(_("End time must be greater than start time for %s map <%s>") % (self.get_type(), self.get_id()))
+            else:
+                # Do not create an interval in case start and end time are equal
+                if start_time == end_time:
+                    end_time = None
 
         self.base.set_ttype("absolute")
         
@@ -73,8 +76,6 @@
         self.absolute_time.set_end_time(end_time)
         self.absolute_time.set_timezone(timezone)
 
-        return True
-
     def update_absolute_time(self, start_time, end_time=None, timezone=None, dbif = None):
         """Update the absolute time
 
@@ -103,10 +104,13 @@
            @param end_time: A double value in days
 
         """
-        if start_time != None and end_time != None:
-            if abs(float(start_time)) >= abs(float(end_time)):
-                core.error(_("End time must be greater than start time"))
-                return False
+        if start_time and end_time:
+            if abs(float(start_time)) > abs(float(end_time)):
+                core.fatal(_("End time must be greater than start time for %s map <%s>") % (self.get_type(), self.get_id()))
+            else:
+                # Do not create an interval in case start and end time are equal
+                if start_time == end_time:
+                    end_time = None
 
         self.base.set_ttype("relative")
         
@@ -116,8 +120,6 @@
         else:
             self.relative_time.set_end_time(None)
 
-        return True
-
     def update_relative_time(self, start_time, end_time=None, dbif = None):
         """Update the relative time interval
 
@@ -152,6 +154,24 @@
         """
         self.spatial_extent.set_spatial_extent(north, south, east, west, top, bottom)
         
+    def check_valid_time(self):
+        """Check for correct valid time"""
+        if self.is_time_absolute():
+            start, end, tz = self.get_absolute_time()
+        else:
+            start, end = self.get_relative_time()
+
+        if start:
+            if end:
+                if start >= end:
+                    core.error(_("Map <%s> has incorrect time interval, start time is greater than end time") % (self.get_id()))
+                    return False
+        else:
+            core.error(_("Map <%s> has incorrect start time") % (self.get_id()))
+            return False
+
+        return True
+
     def delete(self, dbif=None):
 	"""Delete a map entry from database if it exists
         
@@ -197,7 +217,7 @@
         if connect == True:
             dbif.close()
 
-    def unregister(self, dbif=None):
+    def unregister(self, dbif=None, update=True):
 	""" Remove the map entry in each space time dataset in which this map is registered
 
            @param dbif: The database interface to be used
@@ -225,7 +245,8 @@
                 stds.unregister_map(self, dbif)
                 # Take care to update the space time dataset after
                 # the map has been unregistred
-                stds.update_from_registered_maps(dbif)
+                if update == True:
+                    stds.update_from_registered_maps(dbif)
 
         dbif.connection.commit()
 

Modified: grass/trunk/lib/python/temporal/abstract_space_time_dataset.py
===================================================================
--- grass/trunk/lib/python/temporal/abstract_space_time_dataset.py	2011-10-14 15:22:13 UTC (rev 48795)
+++ grass/trunk/lib/python/temporal/abstract_space_time_dataset.py	2011-10-14 15:22:54 UTC (rev 48796)
@@ -107,6 +107,19 @@
 
         return granularity, temporal_type, semantic_type, title, description
 
+    def get_map_time(self):
+        """Return the type of the map time, interval, point, maixed or invalid"""
+        
+        temporal_type = self.get_temporal_type()
+
+        if temporal_type == "absolute":
+            map_time   = self.absolute_time.get_map_time()
+        elif temporal_type == "relative":
+            map_time   = self.relative_time.get_map_time()
+
+        return map_time
+
+
     def print_temporal_relation_matrix(self, maps):
         """Print the temporal relation matrix of all registered maps to stdout
 
@@ -116,14 +129,28 @@
            @param dbif: The database interface to be used
         """
 
-        for map in maps:
-            print map.get_id(),
-        print " "    
+        for i in range(len(maps)):
+            reltations = ""
+            count = 0
+            for j in range(i + 1, len(maps)):
+                relation = maps[j].temporal_relation(maps[i])
 
-        for mapA in maps:
-            for mapB in maps:
-                print mapA.temporal_relation(mapB),
-            print " "
+                # print maps[j].base.get_name(), maps[i].base.get_name(), relation
+                if count == 0:
+                    relations = relation
+                else:
+                    relations += "," + relation
+                count += 1
+                # Break if the the next map follows
+                if relation == "follows":
+                    break
+                # Break if the the next map is after
+                if relation == "after":
+                    break
+            if i < len(maps) - 1:    
+                print maps[i].base.get_name(), relations    
+            else:
+                print maps[i].base.get_name()
 
     def get_temporal_relation_matrix(self, maps):
         """Return the temporal relation matrix of all registered maps as listof lists
@@ -133,7 +160,7 @@
            The temproal relation matrix includes the temporal relations between
            all registered maps. The relations are strings stored in a list of lists.
            
-           @param dbif: The database interface to be used
+           @param maps: a ordered by start_time list of map objects
         """
 
         matrix = []
@@ -153,7 +180,7 @@
 
         return matrix
 
-    def get_temporal_map_type_count(self, maps):
+    def count_temporal_types(self, maps):
         """Return the temporal type of the registered maps as dictionary
 
            The map list must be ordered by start time
@@ -162,9 +189,8 @@
            * point    -> only the start time is present
            * interval -> start and end time
            * invalid  -> No valid time point or interval found
-           * holes    -> In case the maps are interval
 
-           @param dbif: The database interface to be used
+           @param maps: A sorted (start_time) list of abstract_dataset objects
         """
 
         time_invalid = 0
@@ -181,11 +207,6 @@
 
             if start and end:
                 time_interval += 1
-                # Check for holes
-                if i < len(maps) - 1:
-                    relation = maps[i + 1].temporal_relation(maps[i])
-                    if relation != "follows":
-                        holes += 1
             elif start and not end:
                 time_point += 1
             else:
@@ -195,26 +216,37 @@
         tcount["interval"] = time_interval
         tcount["invalid"] = time_invalid
 
-        holes = 0
+        return tcount
 
-        # Check for holes
-        if time_interval > 0 and time_point == 0 and time_invalid == 0:
+    def count_gaps(self, maps, is_interval = False):
+        """Count the number of gaps between temporal neighbours. The maps must have intervals as valid time
+        
+           @param maps: A sorted (start_time) list of abstract_dataset objects
+           @param is_interval: Set true if the maps have vaild interval time (relative or absolute)
+           @return The numbers of gaps between temporal neighbours
+        """
+
+        gaps = 0
+
+        # Check for gaps
+        if is_interval:    
             for i in range(len(maps)):
                 if i < len(maps) - 1:
                     relation = maps[i + 1].temporal_relation(maps[i])
-                    if relation != "follows":
-                        holes += 1
+                    if relation == "after":
+                        gaps += 1
+        else:
+            gaps = None # Gaps only possible in temporal consistent datasets (only intervals)
 
-        tcount["holes"] = holes
+        return gaps
 
-        return tcount
-
-    def get_temporal_relations_count(self, maps):
+    def count_temporal_relations(self, maps):
         """Count the temporal relations between the registered maps.
 
            The map list must be ordered by start time
 
-           @param dbif: The database interface to be used
+           @param maps: A sorted (start_time) list of abstract_dataset objects
+           @return A dictionary with counted temporal relationships
         """
 
         tcount = {}
@@ -228,8 +260,77 @@
                 else:
                     tcount[relation] = 1
 
+                # Break if the the next map follows
+                if relation == "follows":
+                    break
+                # Break if the the next map is after
+                if relation == "after":
+                    break
+
         return tcount
 
+    def check_temporal_topology(self, maps=None, dbif=None):
+        """Check the temporal topology
+
+           Correct topology means, that time intervals are not overlap or
+           that intervals does not contain other intervals. Equal time intervals or
+           points of time are not allowed.
+
+           The map list must be ordered by start time
+
+           Allowed and not allowed temporal relationships for correct topology
+           after      -> allowed
+           before     -> allowed
+           follows    -> allowed
+           precedes   -> allowed
+
+           equivalent -> not allowed
+           during     -> not allowed
+           contains   -> not allowed
+           overlaps   -> not allowed
+           overlapped -> not allowed
+           starts     -> not allowed
+           finishes   -> not allowed
+           started    -> not allowed
+           finished   -> not allowed
+
+           @param maps: A sorted (start_time) list of abstract_dataset objects
+           @return True if topology is correct
+        """
+        if maps == None:
+            maps = get_registered_maps_as_objects(where=None, order="start_time", dbif=dbif)
+
+        relations = self.count_temporal_relations(maps)
+
+        map_time = self.get_map_time()
+
+        if map_time == "interval" or map_time == "mixed":
+            if relations.has_key("equivalent"):
+                return False
+            if relations.has_key("during"):
+                return False
+            if relations.has_key("contains"):
+                return False
+            if relations.has_key("overlaps"):
+                return False
+            if relations.has_key("overlapped"):
+                return False
+            if relations.has_key("starts"):
+                return False
+            if relations.has_key("finishes"):
+                return False
+            if relations.has_key("started"):
+                return False
+            if relations.has_key("finished"):
+                return False
+        elif map_time == "point":
+            if relations.has_key("equivalent"):
+                return False
+        else:
+            return False
+
+        return True
+
     def get_registered_maps_as_objects(self, where=None, order="start_time", dbif=None):
         """Return all registered maps as ordered object list
 
@@ -390,6 +491,10 @@
 
         # First select all data from the database
         map.select(dbif)
+
+        if not map.check_valid_time():
+            core.fatal(_("Map <%s> has invalid time") % map.get_id())
+
         map_id = map.base.get_id()
         map_name = map.base.get_name()
         map_mapset = map.base.get_mapset()
@@ -702,10 +807,7 @@
 		    max_start_time = row[0]
 
 		if end_time < max_start_time:
-                    map_time = "mixed"
 		    use_start_time = True
-                else:
-                    map_time = "interval"
 		    
         # Set the maximum start time as end time
         if use_start_time:
@@ -733,9 +835,18 @@
 	    else:
 		dbif.cursor.execute(sql)
 
-            if end_time == None:
-                map_time = "point"
+        # Count the temporal map types
+        tlist = self.count_temporal_types(self.get_registered_maps_as_objects(dbif=dbif))
 
+        if tlist["interval"] > 0 and tlist["point"] == 0 and tlist["invalid"] == 0:
+            map_time = "interval"
+        elif tlist["interval"] == 0 and tlist["point"] > 0 and tlist["invalid"] == 0:
+            map_time = "point"
+        elif tlist["interval"] > 0 and tlist["point"] > 0 and tlist["invalid"] == 0:
+            map_time = "mixed"
+        else:
+            map_time = "invalid"
+
         # Set the map time type
         if self.is_time_absolute():
             self.absolute_time.select(dbif)

Modified: grass/trunk/lib/python/temporal/datetime_math.py
===================================================================
--- grass/trunk/lib/python/temporal/datetime_math.py	2011-10-14 15:22:13 UTC (rev 48795)
+++ grass/trunk/lib/python/temporal/datetime_math.py	2011-10-14 15:22:54 UTC (rev 48796)
@@ -26,6 +26,17 @@
 
 ###############################################################################
 
+def datetime_delta_to_double(dt1, dt2):
+    """Compute the the dfference dt2 - dt1 and convert the time delta into a 
+       double value, representing days.
+    """
+
+    delta = dt2 - dt1
+
+    return float(delta.days) + float(delta.seconds/86400.0)
+
+###############################################################################
+
 def increment_datetime_by_string(mydate, increment, mult = 1):
     """Return a new datetime object incremented with the provided relative dates specified as string.
        Additional a multiplier can be specified to multiply the increment bevor adding to the provided datetime object.

Modified: grass/trunk/lib/python/temporal/space_time_datasets_tools.py
===================================================================
--- grass/trunk/lib/python/temporal/space_time_datasets_tools.py	2011-10-14 15:22:13 UTC (rev 48795)
+++ grass/trunk/lib/python/temporal/space_time_datasets_tools.py	2011-10-14 15:22:54 UTC (rev 48796)
@@ -77,12 +77,7 @@
     else:
         id = name
 
-    if type == "rast":
-        sp = space_time_raster_dataset(id)
-    if type == "rast3d":
-        sp = space_time_raster3d_dataset(id)
-    if type == "vect":
-        sp = space_time_vector_dataset(id)
+    sp = dataset_factory(type, id)
 
     connect = False
 
@@ -232,12 +227,7 @@
         else:
             id = name
 
-        if type == "rast":
-            sp = space_time_raster_dataset(id)
-        if type == "rast3d":
-            sp = space_time_raster3d_dataset(id)
-        if type == "vect":
-            sp = space_time_vector_dataset(id)
+        sp = dataset_factory(type, id)
 
         if sp.is_in_db(dbif) == False:
             dbif.close()
@@ -418,12 +408,7 @@
             else:
                 mapid = entry
 
-        if type == "rast":
-            map = raster_dataset(mapid)
-        if type == "rast3d":
-            map = raster3d_dataset(mapid)
-        if type == "vect":
-            map = vector_dataset(mapid)
+        sp = dataset_factory(type, id)
 
         # Use the time data from file
         if start_time_in_file:
@@ -530,3 +515,31 @@
 
     if connect == True:
         dbif.close()
+
+###############################################################################
+
+def dataset_factory(type, id):
+    """A factory functions to create space time or map datasets
+    
+       @param type: the dataset type: rast, rast3d, vect, strds, str3ds, stvds
+       @param id: The id of the dataset ("name at mapset")
+    """
+    print type, id
+    if type == "strds":
+        sp = space_time_raster_dataset(id)
+    elif type == "str3ds":
+        sp = space_time_raster3d_dataset(id)
+    elif type == "stvds":
+        sp = space_time_vector_dataset(id)
+    elif type == "rast":
+        sp = raster_dataset(id)
+    elif type == "rast3d":
+        sp = raster3d_dataset(id)
+    elif type == "vect":
+        sp = vector_dataset(id)
+    else:
+        core.error(_("Unknown dataset type: %s") % type)
+        return None
+
+    return sp
+

Modified: grass/trunk/lib/python/temporal/temporal_extent.py
===================================================================
--- grass/trunk/lib/python/temporal/temporal_extent.py	2011-10-14 15:22:13 UTC (rev 48795)
+++ grass/trunk/lib/python/temporal/temporal_extent.py	2011-10-14 15:22:54 UTC (rev 48796)
@@ -164,8 +164,9 @@
 	   A   |-------|
 	   B  |---------|
 	"""
-        if  self.D["end_time"] == None and map.D["end_time"] == None :
-            return False
+        # Check single point of time in interval
+        if  map.D["end_time"] == None:
+                return False
 
         # Check single point of time in interval
         if  self.D["end_time"] == None:
@@ -184,8 +185,9 @@
 	   A  |---------|
 	   B   |-------|
 	"""
-        if  self.D["end_time"] == None and map.D["end_time"] == None :
-            return False
+        # Check single point of time in interval
+        if  self.D["end_time"] == None:
+                return False
 
         # Check single point of time in interval
         if  map.D["end_time"] == None:
@@ -250,6 +252,20 @@
 	"""Returns the temporal relation between temporal objects
 	   Temporal relationsships are implemented after [Allen and Ferguson 1994 Actions and Events in Interval Temporal Logic]
 	"""
+        
+        # First check for correct time
+        if not self.D.has_key("start_time"):
+            return None
+        if not self.D.has_key("end_time"):
+            return None
+        if not map.D.has_key("start_time"):
+            return None
+        if not map.D.has_key("end_time"):
+            return None
+
+        if self.D["start_time"] == None or map.D["start_time"] == None:
+            return None
+
 	if self.equivalent(map):
 	    return "equivalent"
 	if self.during(map):



More information about the grass-commit mailing list