[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