abstract_map_dataset.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. # -*- coding: utf-8 -*-
  2. """!@package grass.temporal
  3. @brief GRASS Python scripting module (temporal GIS functions)
  4. Temporal GIS related functions to be used in temporal GIS Python library package.
  5. Usage:
  6. @code
  7. import grass.temporal as tgis
  8. ...
  9. @endcode
  10. (C) 2008-2011 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. @author Soeren Gebbert
  15. """
  16. from abstract_dataset import *
  17. from datetime_math import *
  18. ###############################################################################
  19. class abstract_map_dataset(abstract_dataset):
  20. """This is the base class for all maps (raster, vector, raster3d)
  21. providing additional function to set the valid time and the spatial extent.
  22. """
  23. def get_new_stds_instance(self, ident):
  24. """Return a new space time dataset instance in which maps are stored with the type of this class
  25. @param ident: The identifier of the dataset
  26. """
  27. raise IOError("This method must be implemented in the subclasses")
  28. def get_stds_register(self):
  29. """Return the space time dataset register table name in which stds are listed in which this map is registered"""
  30. raise IOError("This method must be implemented in the subclasses")
  31. def set_stds_register(self, name):
  32. """Set the space time dataset register table name.
  33. This table stores all space time datasets in which this map is registered.
  34. @param ident: The name of the register table
  35. """
  36. raise IOError("This method must be implemented in the subclasses")
  37. def get_timestamp_module_name(self):
  38. """Return the name of the C-module to set the time stamp in the file system"""
  39. raise IOError("This method must be implemented in the subclasses")
  40. def load(self):
  41. """Load the content of this object from map files"""
  42. raise IOError("This method must be implemented in the subclasses")
  43. def print_info(self):
  44. """Print information about this class in human readable style"""
  45. if self.get_type() == "raster":
  46. # 1 2 3 4 5 6 7
  47. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  48. print ""
  49. print " +-------------------- Raster Dataset ----------------------------------------+"
  50. if self.get_type() == "raster3d":
  51. # 1 2 3 4 5 6 7
  52. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  53. print ""
  54. print " +-------------------- Raster3d Dataset --------------------------------------+"
  55. if self.get_type() == "vector":
  56. # 1 2 3 4 5 6 7
  57. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  58. print ""
  59. print " +-------------------- Vector Dataset ----------------------------------------+"
  60. print " | |"
  61. self.base.print_info()
  62. if self.is_time_absolute():
  63. self.absolute_time.print_info()
  64. if self.is_time_relative():
  65. self.relative_time.print_info()
  66. self.spatial_extent.print_info()
  67. self.metadata.print_info()
  68. datasets = self.get_registered_datasets()
  69. count = 0
  70. string = ""
  71. for ds in datasets:
  72. if count == 0:
  73. string += ds["id"]
  74. else:
  75. string += ",%s" % ds["id"]
  76. count += 1
  77. if count > 2:
  78. string += " | ............................ "
  79. print " | Registered datasets ........ " + string
  80. print " +----------------------------------------------------------------------------+"
  81. def print_shell_info(self):
  82. """Print information about this class in shell style"""
  83. self.base.print_shell_info()
  84. if self.is_time_absolute():
  85. self.absolute_time.print_shell_info()
  86. if self.is_time_relative():
  87. self.relative_time.print_shell_info()
  88. self.spatial_extent.print_shell_info()
  89. self.metadata.print_shell_info()
  90. datasets = self.get_registered_datasets()
  91. count = 0
  92. string = ""
  93. for ds in datasets:
  94. if count == 0:
  95. string += ds["id"]
  96. else:
  97. string += ",%s" % ds["id"]
  98. count += 1
  99. print "registered_datasets=" + string
  100. def set_absolute_time(self, start_time, end_time=None, timezone=None):
  101. """Set the absolute time interval with start time and end time
  102. @param start_time: a datetime object specifying the start time of the map
  103. @param end_time: a datetime object specifying the end time of the map
  104. @param timezone: Thee timezone of the map
  105. """
  106. if start_time and not isinstance(start_time, datetime) :
  107. core.fatal(_("Start time must be of type datetime for %s map <%s>") % (self.get_type(), self.get_id()))
  108. if end_time and not isinstance(end_time, datetime) :
  109. core.fatal(_("End time must be of type datetime for %s map <%s>") % (self.get_type(), self.get_id()))
  110. if start_time and end_time:
  111. if start_time > end_time:
  112. core.fatal(_("End time must be greater than start time for %s map <%s>") % (self.get_type(), self.get_id()))
  113. else:
  114. # Do not create an interval in case start and end time are equal
  115. if start_time == end_time:
  116. end_time = None
  117. self.base.set_ttype("absolute")
  118. self.absolute_time.set_start_time(start_time)
  119. self.absolute_time.set_end_time(end_time)
  120. self.absolute_time.set_timezone(timezone)
  121. def update_absolute_time(self, start_time, end_time=None, timezone=None, dbif = None):
  122. """Update the absolute time
  123. @param start_time: a datetime object specifying the start time of the map
  124. @param end_time: a datetime object specifying the end time of the map
  125. @param timezone: Thee timezone of the map
  126. """
  127. connect = False
  128. if dbif == None:
  129. dbif = sql_database_interface()
  130. dbif.connect()
  131. connect = True
  132. self.set_absolute_time(start_time, end_time, timezone)
  133. self.absolute_time.update_all(dbif)
  134. self.base.update(dbif)
  135. if connect == True:
  136. dbif.close()
  137. # Start the grass C-module to set the time in the file system
  138. start = datetime_to_grass_datetime_string(start_time)
  139. if end_time:
  140. end = datetime_to_grass_datetime_string(end_time)
  141. start += " / %s"%(end)
  142. core.run_command(self.get_timestamp_module_name(), map=self.get_id(), date=start)
  143. def set_relative_time(self, start_time, end_time=None):
  144. """Set the relative time interval
  145. @param start_time: A double value in days
  146. @param end_time: A double value in days
  147. """
  148. if start_time != None and end_time != None:
  149. if abs(float(start_time)) > abs(float(end_time)):
  150. core.fatal(_("End time must be greater than start time for %s map <%s>") % (self.get_type(), self.get_id()))
  151. else:
  152. # Do not create an interval in case start and end time are equal
  153. if start_time == end_time:
  154. end_time = None
  155. self.base.set_ttype("relative")
  156. self.relative_time.set_start_time(float(start_time))
  157. if end_time != None:
  158. self.relative_time.set_end_time(float(end_time))
  159. else:
  160. self.relative_time.set_end_time(None)
  161. def update_relative_time(self, start_time, end_time=None, dbif = None):
  162. """Update the relative time interval
  163. @param start_time: A double value
  164. @param end_time: A double value
  165. @param dbif: The database interface to be used
  166. """
  167. connect = False
  168. if dbif == None:
  169. dbif = sql_database_interface()
  170. dbif.connect()
  171. connect = True
  172. self.set_relative_time(start_time, end_time)
  173. self.relative_time.update_all(dbif)
  174. self.base.update(dbif)
  175. dbif.connection.commit()
  176. if connect == True:
  177. dbif.close()
  178. def set_spatial_extent(self, north, south, east, west, top=0, bottom=0):
  179. """Set the spatial extent of the map
  180. @param north: The northern edge
  181. @param south: The southern edge
  182. @param east: The eastern edge
  183. @param west: The western edge
  184. @param top: The top edge
  185. @param bottom: The bottom ege
  186. """
  187. self.spatial_extent.set_spatial_extent(north, south, east, west, top, bottom)
  188. def check_valid_time(self):
  189. """Check for correct valid time"""
  190. if self.is_time_absolute():
  191. start, end, tz = self.get_absolute_time()
  192. else:
  193. start, end = self.get_relative_time()
  194. if start != None:
  195. if end != None:
  196. if start >= end:
  197. core.error(_("Map <%s> has incorrect time interval, start time is greater than end time") % (self.get_id()))
  198. return False
  199. else:
  200. core.error(_("Map <%s> has incorrect start time") % (self.get_id()))
  201. return False
  202. return True
  203. def delete(self, dbif=None):
  204. """Delete a map entry from database if it exists
  205. Remove dependent entries:
  206. * Remove the map entry in each space time dataset in which this map is registered
  207. * Remove the space time dataset register table
  208. @param dbif: The database interface to be used
  209. """
  210. connect = False
  211. if dbif == None:
  212. dbif = sql_database_interface()
  213. dbif.connect()
  214. connect = True
  215. if self.is_in_db(dbif):
  216. # SELECT all needed informations from the database
  217. self.select(dbif)
  218. # First we unregister from all dependent space time datasets
  219. self.unregister(dbif)
  220. # Remove the strds register table
  221. if self.get_stds_register():
  222. sql = "DROP TABLE " + self.get_stds_register()
  223. #print sql
  224. try:
  225. dbif.cursor.execute(sql)
  226. except:
  227. core.error(_("Unable to remove space time dataset register table <%s>") % (self.get_stds_register()))
  228. core.verbose(_("Delete %s dataset <%s> from temporal database") % (self.get_type(), self.get_id()))
  229. # Delete yourself from the database, trigger functions will take care of dependencies
  230. self.base.delete(dbif)
  231. # Remove the timestamp from the file system
  232. core.run_command(self.get_timestamp_module_name(), map=self.get_id(), date="none")
  233. self.reset(None)
  234. dbif.connection.commit()
  235. if connect == True:
  236. dbif.close()
  237. def unregister(self, dbif=None, update=True):
  238. """ Remove the map entry in each space time dataset in which this map is registered
  239. @param dbif: The database interface to be used
  240. @param update: Call for each unregister statement the update_from_registered_maps
  241. of the space time dataset. This can slow down the unregistration process significantly.
  242. """
  243. core.verbose(_("Unregister %s dataset <%s> from space time datasets") % (self.get_type(), self.get_id()))
  244. connect = False
  245. if dbif == None:
  246. dbif = sql_database_interface()
  247. dbif.connect()
  248. connect = True
  249. # Get all datasets in which this map is registered
  250. rows = self.get_registered_datasets(dbif)
  251. # For each stds in which the map is registered
  252. if rows:
  253. count = 0
  254. num_sps = len(rows)
  255. for row in rows:
  256. core.percent(count, num_sps, 1)
  257. count += 1
  258. # Create a space time dataset object to remove the map
  259. # from its register
  260. stds = self.get_new_stds_instance(row["id"])
  261. stds.select(dbif)
  262. stds.unregister_map(self, dbif)
  263. # Take care to update the space time dataset after
  264. # the map has been unregistred
  265. if update == True:
  266. stds.update_from_registered_maps(dbif)
  267. core.percent(1, 1, 1)
  268. dbif.connection.commit()
  269. if connect == True:
  270. dbif.close()
  271. def get_registered_datasets(self, dbif=None):
  272. """Return all space time dataset ids in which this map is registered as
  273. dictionary like rows with column "id" or None if this map is not registered in any
  274. space time dataset.
  275. @param dbif: The database interface to be used
  276. """
  277. connect = False
  278. if dbif == None:
  279. dbif = sql_database_interface()
  280. dbif.connect()
  281. connect = True
  282. rows = None
  283. try:
  284. if self.get_stds_register() != None:
  285. # Select all stds tables in which this map is registered
  286. sql = "SELECT id FROM " + self.get_stds_register()
  287. dbif.cursor.execute(sql)
  288. rows = dbif.cursor.fetchall()
  289. except:
  290. core.error(_("Unable to select space time dataset register table <%s>") % (self.get_stds_register()))
  291. if connect == True:
  292. dbif.close()
  293. return rows