abstract_dataset.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. >>> ad = AbstractDataset()
  9. >>> ad.reset(ident="soil@PERMANENT")
  10. Traceback (most recent call last):
  11. File "/usr/lib/python2.7/doctest.py", line 1289, in __run
  12. compileflags, 1) in test.globs
  13. File "<doctest __main__[2]>", line 1, in <module>
  14. ad.reset(ident="soil@PERMANENT")
  15. File "AbstractDataset.py", line 53, in reset
  16. raise ImplementationError("This method must be implemented in the subclasses")
  17. ImplementationError: 'This method must be implemented in the subclasses'
  18. @endcode
  19. (C) 2011-2012 by the GRASS Development Team
  20. This program is free software under the GNU General Public
  21. License (>=v2). Read the file COPYING that comes with GRASS
  22. for details.
  23. @author Soeren Gebbert
  24. """
  25. import uuid
  26. import copy
  27. from temporal_extent import *
  28. from spatial_extent import *
  29. from metadata import *
  30. class ImplementationError(Exception):
  31. """!Exception raised for the calling of methods that should be implemented in
  32. sub classes.
  33. """
  34. def __init__(self, msg):
  35. self.msg = msg
  36. def __str__(self):
  37. return repr(self.msg)
  38. ###############################################################################
  39. class AbstractDataset(object):
  40. """!This is the base class for all datasets
  41. (raster, vector, raster3d, strds, stvds, str3ds)"""
  42. def __init__(self):
  43. pass
  44. def reset(self, ident):
  45. """!Reset the internal structure and set the identifier
  46. @param ident: The identifier of the dataset
  47. """
  48. raise ImplementationError("This method must be implemented in the subclasses")
  49. def get_type(self):
  50. """!Return the type of this class"""
  51. raise ImplementationError("This method must be implemented in the subclasses")
  52. def get_new_instance(self, ident):
  53. """!Return a new instance with the type of this class
  54. @param ident: The identifier of the dataset
  55. """
  56. raise ImplementationError("This method must be implemented in the subclasses")
  57. def spatial_overlapping(self, dataset):
  58. """!Return True if the spatial extents are overlapping"""
  59. raise ImplementationError("This method must be implemented in the subclasses")
  60. def spatial_relation(self, dataset):
  61. """Return the spatial relationship between self and dataset"""
  62. raise ImplementationError("This method must be implemented in the subclasses")
  63. def print_info(self):
  64. """!Print information about this class in human readable style"""
  65. raise ImplementationError("This method must be implemented in the subclasses")
  66. def print_shell_info(self):
  67. """!Print information about this class in shell style"""
  68. raise ImplementationError("This method must be implemented in the subclasses")
  69. def print_self(self):
  70. """!Print the content of the internal structure to stdout"""
  71. raise ImplementationError("This method must be implemented in the subclasses")
  72. def set_id(self, ident):
  73. self.base.set_id(ident)
  74. if self.is_time_absolute():
  75. self.absolute_time.set_id(ident)
  76. if self.is_time_relative():
  77. self.relative_time.set_id(ident)
  78. self.spatial_extent.set_id(ident)
  79. self.metadata.set_id(ident)
  80. def get_id(self):
  81. """!Return the unique identifier of the dataset"""
  82. return self.base.get_id()
  83. def get_name(self):
  84. """!Return the name"""
  85. return self.base.get_name()
  86. def get_mapset(self):
  87. """!Return the mapset"""
  88. return self.base.get_mapset()
  89. def get_valid_time(self):
  90. """!Returns a tuple of the valid start and end time
  91. Start and end time can be either of type datetime or of type double
  92. depending on the temporal type
  93. @return A tuple of (start_time, end_time)
  94. """
  95. start = None
  96. end = None
  97. if self.is_time_absolute():
  98. start = self.absolute_time.get_start_time()
  99. end = self.absolute_time.get_end_time()
  100. if self.is_time_relative():
  101. start = self.relative_time.get_start_time()
  102. end = self.relative_time.get_end_time()
  103. return (start, end)
  104. def get_absolute_time(self):
  105. """!Returns a tuple of the start, the end
  106. valid time and the timezone of the map
  107. @return A tuple of (start_time, end_time, timezone)
  108. """
  109. start = self.absolute_time.get_start_time()
  110. end = self.absolute_time.get_end_time()
  111. tz = self.absolute_time.get_timezone()
  112. return (start, end, tz)
  113. def get_relative_time(self):
  114. """!Returns the valid relative time interval (start_time, end_time, unit)
  115. or None if not present"""
  116. start = self.relative_time.get_start_time()
  117. end = self.relative_time.get_end_time()
  118. unit = self.relative_time.get_unit()
  119. return (start, end, unit)
  120. def get_relative_time_unit(self):
  121. """!Returns the relative time unit or None if not present"""
  122. unit = self.relative_time.get_unit()
  123. return unit
  124. def check_relative_time_unit(self, unit):
  125. """!Check if unit is of type years, months, days, hours,
  126. minutes or seconds
  127. Return True if success or False otherwise
  128. """
  129. # Check unit
  130. units = ["year", "years", "month", "months", "day", "days", "hour",
  131. "hours", "minute", "minutes", "second", "seconds"]
  132. if unit not in units:
  133. return False
  134. return True
  135. def get_temporal_type(self):
  136. """!Return the temporal type of this dataset"""
  137. return self.base.get_ttype()
  138. def get_spatial_extent(self):
  139. """!Return a tuple of spatial extent
  140. (north, south, east, west, top, bottom) """
  141. return self.spatial_extent.get_spatial_extent()
  142. def select(self, dbif=None):
  143. """!Select temporal dataset entry from database and fill
  144. up the internal structure"""
  145. dbif, connected = init_dbif(dbif)
  146. self.base.select(dbif)
  147. if self.is_time_absolute():
  148. self.absolute_time.select(dbif)
  149. if self.is_time_relative():
  150. self.relative_time.select(dbif)
  151. self.spatial_extent.select(dbif)
  152. self.metadata.select(dbif)
  153. if connected:
  154. dbif.close()
  155. def is_in_db(self, dbif=None):
  156. """!Check if the temporal dataset entry is in the database
  157. @param dbif: The database interface to be used
  158. """
  159. return self.base.is_in_db(dbif)
  160. def delete(self):
  161. """!Delete temporal dataset entry from database if it exists"""
  162. raise ImplementationError("This method must be implemented in the subclasses")
  163. def insert(self, dbif=None, execute=True):
  164. """!Insert temporal dataset entry into
  165. database from the internal structure
  166. @param dbif: The database interface to be used
  167. @param execute: If True the SQL statements will be executed.
  168. If False the prepared SQL statements are returned
  169. and must be executed by the caller.
  170. """
  171. dbif, connected = init_dbif(dbif)
  172. # Build the INSERT SQL statement
  173. statement = self.base.get_insert_statement_mogrified(dbif)
  174. if self.is_time_absolute():
  175. statement += self.absolute_time.get_insert_statement_mogrified(
  176. dbif)
  177. if self.is_time_relative():
  178. statement += self.relative_time.get_insert_statement_mogrified(
  179. dbif)
  180. statement += self.spatial_extent.get_insert_statement_mogrified(dbif)
  181. statement += self.metadata.get_insert_statement_mogrified(dbif)
  182. if execute:
  183. dbif.execute_transaction(statement)
  184. if connected:
  185. dbif.close()
  186. return ""
  187. if connected:
  188. dbif.close()
  189. return statement
  190. def update(self, dbif=None, execute=True, ident=None):
  191. """!Update temporal dataset entry of database from the internal structure
  192. excluding None variables
  193. @param dbif: The database interface to be used
  194. @param execute: If True the SQL statements will be executed.
  195. If False the prepared SQL statements are returned
  196. and must be executed by the caller.
  197. @param ident: The identifier to be updated, useful for renaming
  198. """
  199. dbif, connected = init_dbif(dbif)
  200. # Build the UPDATE SQL statement
  201. statement = self.base.get_update_statement_mogrified(dbif, ident)
  202. if self.is_time_absolute():
  203. statement += self.absolute_time.get_update_statement_mogrified(
  204. dbif, ident)
  205. if self.is_time_relative():
  206. statement += self.relative_time.get_update_statement_mogrified(
  207. dbif, ident)
  208. statement += self.spatial_extent.get_update_statement_mogrified(dbif,
  209. ident)
  210. statement += self.metadata.get_update_statement_mogrified(dbif, ident)
  211. if execute:
  212. dbif.execute_transaction(statement)
  213. if connected:
  214. dbif.close()
  215. return ""
  216. if connected:
  217. dbif.close()
  218. return statement
  219. def update_all(self, dbif=None, execute=True, ident=None):
  220. """!Update temporal dataset entry of database from the internal structure
  221. and include None variables.
  222. @param dbif: The database interface to be used
  223. @param execute: If True the SQL statements will be executed.
  224. If False the prepared SQL statements are returned
  225. and must be executed by the caller.
  226. @param ident: The identifier to be updated, useful for renaming
  227. """
  228. dbif, connected = init_dbif(dbif)
  229. # Build the UPDATE SQL statement
  230. statement = self.base.get_update_all_statement_mogrified(dbif, ident)
  231. if self.is_time_absolute():
  232. statement += self.absolute_time.get_update_all_statement_mogrified(
  233. dbif, ident)
  234. if self.is_time_relative():
  235. statement += self.relative_time.get_update_all_statement_mogrified(
  236. dbif, ident)
  237. statement += self.spatial_extent.get_update_all_statement_mogrified(
  238. dbif, ident)
  239. statement += self.metadata.get_update_all_statement_mogrified(dbif, ident)
  240. if execute:
  241. dbif.execute_transaction(statement)
  242. if connected:
  243. dbif.close()
  244. return ""
  245. if connected:
  246. dbif.close()
  247. return statement
  248. def set_time_to_absolute(self):
  249. """!Set the temporal type to absolute"""
  250. self.base.set_ttype("absolute")
  251. def set_time_to_relative(self):
  252. """!Set the temporal type to relative"""
  253. self.base.set_ttype("relative")
  254. def is_time_absolute(self):
  255. """!Return True in case the temporal type is absolute
  256. @return True if temporal type is absolute, False otherwise
  257. """
  258. if "temporal_type" in self.base.D:
  259. return self.base.get_ttype() == "absolute"
  260. else:
  261. return None
  262. def is_time_relative(self):
  263. """!Return True in case the temporal type is relative
  264. @return True if temporal type is relative, False otherwise
  265. """
  266. if "temporal_type" in self.base.D:
  267. return self.base.get_ttype() == "relative"
  268. else:
  269. return None
  270. def temporal_relation(self, map):
  271. """!Return the temporal relation of this and the provided temporal map"""
  272. if self.is_time_absolute() and map.is_time_absolute():
  273. return self.absolute_time.temporal_relation(map.absolute_time)
  274. if self.is_time_relative() and map.is_time_relative():
  275. return self.relative_time.temporal_relation(map.relative_time)
  276. return None
  277. ###############################################################################
  278. class AbstractDatasetComparisonKeyStartTime(object):
  279. """!This comparison key can be used to sort lists of abstract datasets
  280. by start time
  281. Example:
  282. # Return all maps in a space time raster dataset as map objects
  283. map_list = strds.get_registered_maps_as_objects()
  284. # Sort the maps in the list by start time
  285. sorted_map_list = sorted(
  286. map_list, key=AbstractDatasetComparisonKeyStartTime)
  287. """
  288. def __init__(self, obj, *args):
  289. self.obj = obj
  290. def __lt__(self, other):
  291. startA, endA = self.obj.get_valid_time()
  292. startB, endB = other.obj.get_valid_time()
  293. return startA < startB
  294. def __gt__(self, other):
  295. startA, endA = self.obj.get_valid_time()
  296. startB, endB = other.obj.get_valid_time()
  297. return startA > startB
  298. def __eq__(self, other):
  299. startA, endA = self.obj.get_valid_time()
  300. startB, endB = other.obj.get_valid_time()
  301. return startA == startB
  302. def __le__(self, other):
  303. startA, endA = self.obj.get_valid_time()
  304. startB, endB = other.obj.get_valid_time()
  305. return startA <= startB
  306. def __ge__(self, other):
  307. startA, endA = self.obj.get_valid_time()
  308. startB, endB = other.obj.get_valid_time()
  309. return startA >= startB
  310. def __ne__(self, other):
  311. startA, endA = self.obj.get_valid_time()
  312. startB, endB = other.obj.get_valid_time()
  313. return startA != startB
  314. ###############################################################################
  315. class AbstractDatasetComparisonKeyEndTime(object):
  316. """!This comparison key can be used to sort lists of abstract datasets
  317. by end time
  318. Example:
  319. # Return all maps in a space time raster dataset as map objects
  320. map_list = strds.get_registered_maps_as_objects()
  321. # Sort the maps in the list by end time
  322. sorted_map_list = sorted(
  323. map_list, key=AbstractDatasetComparisonKeyEndTime)
  324. """
  325. def __init__(self, obj, *args):
  326. self.obj = obj
  327. def __lt__(self, other):
  328. startA, endA = self.obj.get_valid_time()
  329. startB, endB = other.obj.get_valid_time()
  330. return endA < endB
  331. def __gt__(self, other):
  332. startA, endA = self.obj.get_valid_time()
  333. startB, endB = other.obj.get_valid_time()
  334. return endA > endB
  335. def __eq__(self, other):
  336. startA, endA = self.obj.get_valid_time()
  337. startB, endB = other.obj.get_valid_time()
  338. return endA == endB
  339. def __le__(self, other):
  340. startA, endA = self.obj.get_valid_time()
  341. startB, endB = other.obj.get_valid_time()
  342. return endA <= endB
  343. def __ge__(self, other):
  344. startA, endA = self.obj.get_valid_time()
  345. startB, endB = other.obj.get_valid_time()
  346. return endA >= endB
  347. def __ne__(self, other):
  348. startA, endA = self.obj.get_valid_time()
  349. startB, endB = other.obj.get_valid_time()
  350. return endA != endB
  351. ###############################################################################
  352. if __name__ == "__main__":
  353. import doctest
  354. doctest.testmod()