abstract_datasets.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. """!@package grass.script.tgis_abstract_datasets
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS related functions to be used in temporal GIS Python library package.
  4. Usage:
  5. @code
  6. from grass.script import tgis_abstract_datasets as grass
  7. ...
  8. @endcode
  9. (C) 2008-2011 by the GRASS Development Team
  10. This program is free software under the GNU General Public
  11. License (>=v2). Read the file COPYING that comes with GRASS
  12. for details.
  13. @author Soeren Gebbert
  14. """
  15. import uuid
  16. import copy
  17. from temporal_extent import *
  18. from spatial_extent import *
  19. from metadata import *
  20. class abstract_dataset(object):
  21. """This is the base class for all datasets (raster, vector, raster3d, strds, stvds, str3ds)"""
  22. def reset(self, ident):
  23. """Reset the internal structure and set the identifier"""
  24. raise IOError("This method must be implemented in the subclasses")
  25. def get_type(self):
  26. """Return the type of this class"""
  27. raise IOError("This method must be implemented in the subclasses")
  28. def get_new_instance(self, ident):
  29. """Return a new instance with the type of this class"""
  30. raise IOError("This method must be implemented in the subclasses")
  31. def get_id(self):
  32. return self.base.get_id()
  33. def get_absolute_time(self):
  34. """Returns a tuple of the start, the end valid time and the timezone of the map
  35. @return A tuple of (start_time, end_time, timezone)
  36. """
  37. start = self.absolute_time.get_start_time()
  38. end = self.absolute_time.get_end_time()
  39. tz = self.absolute_time.get_timezone()
  40. return (start, end, tz)
  41. def get_relative_time(self):
  42. """Returns the relative time interval (start_time, end_time) or None if not present"""
  43. start = self.relative_time.get_start_time()
  44. end = self.relative_time.get_end_time()
  45. return (start, end)
  46. def get_temporal_type(self):
  47. """Return the temporal type of this dataset"""
  48. return self.base.get_ttype()
  49. def get_spatial_extent(self):
  50. """Return a tuple of spatial extent (north, south, east, west, top, bottom) """
  51. north = self.spatial_extent.get_north()
  52. south = self.spatial_extent.get_south()
  53. east = self.spatial_extent.get_east()
  54. west = self.spatial_extent.get_west()
  55. top = self.spatial_extent.get_top()
  56. bottom = self.spatial_extent.get_bottom()
  57. return (north, south, east, west, top, bottom)
  58. def select(self, dbif=None):
  59. """Select temporal dataset entry from database and fill up the internal structure"""
  60. self.base.select(dbif)
  61. if self.is_time_absolute():
  62. self.absolute_time.select(dbif)
  63. if self.is_time_relative():
  64. self.relative_time.select(dbif)
  65. self.spatial_extent.select(dbif)
  66. self.metadata.select(dbif)
  67. def is_in_db(self, dbif=None):
  68. """Check if the temporal dataset entry is in the database"""
  69. return self.base.is_in_db(dbif)
  70. def delete(self):
  71. """Delete temporal dataset entry from database if it exists"""
  72. raise IOError("This method must be implemented in the subclasses")
  73. def insert(self, dbif=None):
  74. """Insert temporal dataset entry into database from the internal structure"""
  75. self.base.insert(dbif)
  76. if self.is_time_absolute():
  77. self.absolute_time.insert(dbif)
  78. if self.is_time_relative():
  79. self.relative_time.insert(dbif)
  80. self.spatial_extent.insert(dbif)
  81. self.metadata.insert(dbif)
  82. def update(self, dbif=None):
  83. """Update temporal dataset entry of database from the internal structure"""
  84. self.base.update(dbif)
  85. if self.is_time_absolute():
  86. self.absolute_time.update(dbif)
  87. if self.is_time_relative():
  88. self.relative_time.update(dbif)
  89. self.spatial_extent.update(dbif)
  90. self.metadata.update(dbif)
  91. def print_self(self):
  92. """Print the content of the internal structure to stdout"""
  93. self.base.print_self()
  94. if self.is_time_absolute():
  95. self.absolute_time.print_self()
  96. if self.is_time_relative():
  97. self.relative_time.print_self()
  98. self.spatial_extent.print_self()
  99. self.metadata.print_self()
  100. def print_info(self):
  101. """Print information about this class in human readable style"""
  102. if self.get_type() == "raster":
  103. # 1 2 3 4 5 6 7
  104. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  105. print ""
  106. print " +-------------------- Raster Dataset ----------------------------------------+"
  107. if self.get_type() == "raster3d":
  108. # 1 2 3 4 5 6 7
  109. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  110. print ""
  111. print " +-------------------- Raster3d Dataset --------------------------------------+"
  112. if self.get_type() == "vector":
  113. # 1 2 3 4 5 6 7
  114. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  115. print ""
  116. print " +-------------------- Vector Dataset ----------------------------------------+"
  117. if self.get_type() == "strds":
  118. # 1 2 3 4 5 6 7
  119. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  120. print ""
  121. print " +-------------------- Space Time Raster Dataset -----------------------------+"
  122. if self.get_type() == "str3ds":
  123. # 1 2 3 4 5 6 7
  124. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  125. print ""
  126. print " +-------------------- Space Time Raster3d Dataset ---------------------------+"
  127. if self.get_type() == "stvds":
  128. # 1 2 3 4 5 6 7
  129. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  130. print ""
  131. print " +-------------------- Space Time Vector Dataset -----------------------------+"
  132. print " | |"
  133. self.base.print_info()
  134. if self.is_time_absolute():
  135. self.absolute_time.print_info()
  136. if self.is_time_relative():
  137. self.relative_time.print_info()
  138. self.spatial_extent.print_info()
  139. self.metadata.print_info()
  140. print " +----------------------------------------------------------------------------+"
  141. def print_shell_info(self):
  142. """Print information about this class in shell style"""
  143. self.base.print_shell_info()
  144. if self.is_time_absolute():
  145. self.absolute_time.print_shell_info()
  146. if self.is_time_relative():
  147. self.relative_time.print_shell_info()
  148. self.spatial_extent.print_shell_info()
  149. self.metadata.print_shell_info()
  150. def set_time_to_absolute(self):
  151. self.base.set_ttype("absolute")
  152. def set_time_to_relative(self):
  153. self.base.set_ttype("relative")
  154. def is_time_absolute(self):
  155. if self.base.D.has_key("temporal_type"):
  156. return self.base.get_ttype() == "absolute"
  157. else:
  158. return None
  159. def is_time_relative(self):
  160. if self.base.D.has_key("temporal_type"):
  161. return self.base.get_ttype() == "relative"
  162. else:
  163. return None
  164. def temporal_relation(self, map):
  165. """Return the temporal relation of this and the provided temporal map"""
  166. if self.is_time_absolute() and map.is_time_absolute():
  167. return self.absolute_time.temporal_relation(map.absolute_time)
  168. if self.is_time_relative() and map.is_time_relative():
  169. return self.relative_time.temporal_relation(map.relative_time)
  170. return None
  171. ###############################################################################
  172. class abstract_map_dataset(abstract_dataset):
  173. """This is the base class for all maps (raster, vector, raster3d)
  174. providing additional function to set the valid time and the spatial extent.
  175. """
  176. def get_new_stds_instance(self, ident):
  177. """Return a new space time dataset instance in which maps are stored with the type of this class"""
  178. raise IOError("This method must be implemented in the subclasses")
  179. def get_stds_register(self):
  180. """Return the space time dataset register table name in which stds are listed in which this map is registered"""
  181. raise IOError("This method must be implemented in the subclasses")
  182. def set_stds_register(self, name):
  183. """Set the space time dataset register table name in which stds are listed in which this map is registered"""
  184. raise IOError("This method must be implemented in the subclasses")
  185. def set_absolute_time(self, start_time, end_time=None, timezone=None):
  186. """Set the absolute time interval with start time and end time
  187. @start_time a datetime object specifying the start time of the map
  188. @end_time a datetime object specifying the end time of the map
  189. @timezone Thee timezone of the map
  190. """
  191. if start_time != None and not isinstance(start_time, datetime) :
  192. core.fatal(_("Start time must be of type datetime"))
  193. if end_time != None and not isinstance(end_time, datetime) :
  194. core.fatal(_("End time must be of type datetime"))
  195. if start_time != None and end_time != None:
  196. if start_time >= end_time:
  197. core.error(_("End time must be later than start time"))
  198. return False
  199. self.base.set_ttype("absolute")
  200. self.absolute_time.set_start_time(start_time)
  201. self.absolute_time.set_end_time(end_time)
  202. self.absolute_time.set_timezone(timezone)
  203. return True
  204. def update_absolute_time(self, start_time, end_time=None, timezone=None, dbif = None):
  205. """Update the absolute time
  206. @start_time a datetime object specifying the start time of the map
  207. @end_time a datetime object specifying the end time of the map
  208. @timezone Thee timezone of the map
  209. """
  210. connect = False
  211. if dbif == None:
  212. dbif = sql_database_interface()
  213. dbif.connect()
  214. connect = True
  215. self.set_absolute_time(start_time, end_time, timezone)
  216. self.absolute_time.update_all(dbif)
  217. self.base.update(dbif)
  218. if connect == True:
  219. dbif.close()
  220. def set_relative_time(self, start_time, end_time=None):
  221. """Set the relative time interval
  222. @start_time A double value in days
  223. @end_time A double value in days
  224. """
  225. if start_time != None and end_time != None:
  226. if abs(float(start_time)) >= abs(float(end_time)):
  227. core.error(_("End time must be greater than start time"))
  228. return False
  229. self.base.set_ttype("relative")
  230. self.relative_time.set_start_time(float(start_time))
  231. if end_time != None:
  232. self.relative_time.set_end_time(float(end_time))
  233. else:
  234. self.relative_time.set_end_time(None)
  235. return True
  236. def update_relative_time(self, start_time, end_time=None, dbif = None):
  237. """Set the relative time interval
  238. @interval A double value in days
  239. """
  240. connect = False
  241. if dbif == None:
  242. dbif = sql_database_interface()
  243. dbif.connect()
  244. connect = True
  245. self.set_relative_time(start_time, end_time)
  246. self.relative_time.update_all(dbif)
  247. self.base.update(dbif)
  248. if connect == True:
  249. dbif.close()
  250. def set_spatial_extent(self, north, south, east, west, top=0, bottom=0):
  251. """Set the spatial extent of the map"""
  252. self.spatial_extent.set_spatial_extent(north, south, east, west, top, bottom)
  253. def delete(self, dbif=None):
  254. """Delete a map entry from database if it exists
  255. Remove dependent entries:
  256. * Remove the map entry in each space time dataset in which this map is registered
  257. * Remove the space time dataset register table
  258. """
  259. connect = False
  260. if dbif == None:
  261. dbif = sql_database_interface()
  262. dbif.connect()
  263. connect = True
  264. if self.is_in_db(dbif):
  265. # First we unregister from all dependent space time datasets
  266. self.unregister(dbif)
  267. # Remove the strds register table
  268. if self.get_stds_register():
  269. sql = "DROP TABLE " + self.get_stds_register()
  270. #print sql
  271. try:
  272. dbif.cursor.execute(sql)
  273. except:
  274. core.error(_("Unable to remove space time dataset register table <%s>") % (self.get_stds_register()))
  275. core.verbose(_("Delete %s dataset <%s> from temporal database") % (self.get_type(), self.get_id()))
  276. # Delete yourself from the database, trigger functions will take care of dependencies
  277. self.base.delete(dbif)
  278. self.reset(None)
  279. if connect == True:
  280. dbif.close()
  281. def unregister(self, dbif=None):
  282. """ Remove the map entry in each space time dataset in which this map is registered
  283. """
  284. core.verbose(_("Unregister %s dataset <%s> from space time datasets") % (self.get_type(), self.get_id()))
  285. connect = False
  286. if dbif == None:
  287. dbif = sql_database_interface()
  288. dbif.connect()
  289. connect = True
  290. # Get all datasets in which this map is registered
  291. rows = self.get_registered_datasets(dbif)
  292. # For each stds in which the map is registered
  293. if rows:
  294. for row in rows:
  295. # Create a space time dataset object to remove the map
  296. # from its register
  297. stds = self.get_new_stds_instance(row["id"])
  298. stds.select(dbif)
  299. stds.unregister_map(self, dbif)
  300. # Take care to update the space time dataset after
  301. # the map has been unregistred
  302. stds.update_from_registered_maps(dbif)
  303. if connect == True:
  304. dbif.close()
  305. def get_registered_datasets(self, dbif=None):
  306. """Return all space time dataset ids in which this map is registered as
  307. sqlite3 rows with column "id" or None if this map is not registered in any
  308. space time dataset.
  309. """
  310. connect = False
  311. if dbif == None:
  312. dbif = sql_database_interface()
  313. dbif.connect()
  314. connect = True
  315. rows = None
  316. try:
  317. if self.get_stds_register() != None:
  318. # Select all stds tables in which this map is registered
  319. sql = "SELECT id FROM " + self.get_stds_register()
  320. dbif.cursor.execute(sql)
  321. rows = dbif.cursor.fetchall()
  322. except:
  323. core.error(_("Unable to select space time dataset register table <%s>") % (self.get_stds_register()))
  324. if connect == True:
  325. dbif.close()
  326. return rows
  327. ###############################################################################
  328. class abstract_space_time_dataset(abstract_dataset):
  329. """Abstract space time dataset class
  330. This class represents a space time dataset. Convenient functions
  331. to select, update, insert or delete objects of this type int the SQL
  332. temporal database exists as well as functions to register or unregister
  333. raster maps.
  334. Parts of the temporal logic are implemented in the SQL temporal database,
  335. like the computation of the temporal and spatial extent as well as the
  336. collecting of metadata.
  337. """
  338. def __init__(self, ident):
  339. self.reset(ident)
  340. def get_new_instance(self, ident):
  341. """Return a new instance with the type of this class"""
  342. raise IOError("This method must be implemented in the subclasses")
  343. def get_new_map_instance(self, ident):
  344. """Return a new instance of a map dataset which is associated with the type of this class"""
  345. raise IOError("This method must be implemented in the subclasses")
  346. def get_map_register(self):
  347. """Return the name of the map register table"""
  348. raise IOError("This method must be implemented in the subclasses")
  349. def set_map_register(self, name):
  350. """Set the name of the map register table"""
  351. raise IOError("This method must be implemented in the subclasses")
  352. def set_initial_values(self, granularity, temporal_type, semantic_type, \
  353. title=None, description=None):
  354. if temporal_type == "absolute":
  355. self.set_time_to_absolute()
  356. self.absolute_time.set_granularity(granularity)
  357. elif temporal_type == "relative":
  358. self.set_time_to_relative()
  359. self.relative_time.set_granularity(granularity)
  360. else:
  361. core.fatal(_("Unknown temporal type \"%s\"") % (temporal_type))
  362. self.base.set_semantic_type(semantic_type)
  363. self.metadata.set_title(title)
  364. self.metadata.set_description(description)
  365. def get_temporal_relation_matrix(self, dbif=None):
  366. """Return the temporal relation matrix between all registered maps
  367. """
  368. connect = False
  369. if dbif == None:
  370. dbif = sql_database_interface()
  371. dbif.connect()
  372. connect = True
  373. matrix = []
  374. maps = self.get_registered_maps_as_objects(dbif=dbif, where=None, order="start_time")
  375. # Create the temporal relation matrix
  376. # Add the map names first
  377. row = []
  378. for map in maps:
  379. row.append(map.get_id())
  380. matrix.append(row)
  381. for mapA in maps:
  382. row = []
  383. for mapB in maps:
  384. row.append(mapA.temporal_relation(mapB))
  385. matrix.append(row)
  386. if connect == True:
  387. dbif.close()
  388. return matrix
  389. def get_registered_maps_as_objects(self, dbif=None, where = None, order = None):
  390. """Return all registered maps as ordered object list
  391. Each row includes all columns specified in the datatype specific view
  392. In case nothing found None is returned
  393. """
  394. connect = False
  395. if dbif == None:
  396. dbif = sql_database_interface()
  397. dbif.connect()
  398. connect = True
  399. obj_list = []
  400. rows = self.get_registered_maps(dbif, where, order)
  401. if rows:
  402. for row in rows:
  403. map = self.get_new_map_instance(row["id"])
  404. map.select(dbif)
  405. obj_list.append(copy.copy(map))
  406. if connect == True:
  407. dbif.close()
  408. return obj_list
  409. def get_registered_maps(self, dbif=None, where = None, order = None):
  410. """Return sqlite rows of all registered maps.
  411. Each row includes all columns specified in the datatype specific view
  412. In case nothing found None is returned
  413. """
  414. connect = False
  415. if dbif == None:
  416. dbif = sql_database_interface()
  417. dbif.connect()
  418. connect = True
  419. rows = None
  420. if self.get_map_register():
  421. # Use the correct temporal table
  422. if self.get_temporal_type() == "absolute":
  423. map_view = self.get_new_map_instance(None).get_type() + "_view_abs_time"
  424. else:
  425. map_view = self.get_new_map_instance(None).get_type() + "_view_rel_time"
  426. sql = "SELECT * FROM %s WHERE %s.id IN (SELECT id FROM %s)" % (map_view, map_view, self.get_map_register())
  427. if where:
  428. sql += " AND %s" % (where)
  429. if order:
  430. sql += " ORDER BY %s" % (order)
  431. print sql
  432. try:
  433. dbif.cursor.execute(sql)
  434. rows = dbif.cursor.fetchall()
  435. except:
  436. core.error(_("Unable to get map ids from register table <%s>") % (self.get_map_register()))
  437. raise
  438. if connect == True:
  439. dbif.close()
  440. return rows
  441. def delete(self, dbif=None):
  442. """Delete a space time dataset from the database"""
  443. # First we need to check if maps are registered in this dataset and
  444. # unregister them
  445. core.verbose(_("Delete space time %s dataset <%s> from temporal database") % (self.get_new_map_instance(ident=None).get_type(), self.get_id()))
  446. connect = False
  447. if dbif == None:
  448. dbif = sql_database_interface()
  449. dbif.connect()
  450. connect = True
  451. if self.get_map_register():
  452. rows = self.get_registered_maps(dbif)
  453. # Unregister each registered map in the table
  454. if rows:
  455. for row in rows:
  456. # Unregister map
  457. map = self.get_new_map_instance(row["id"])
  458. self.unregister_map(map, dbif)
  459. try:
  460. # Drop remove the map register table
  461. sql = "DROP TABLE " + self.get_map_register()
  462. dbif.cursor.execute(sql)
  463. except:
  464. core.error(_("Unable to drop table <%s>") % (self.get_map_register()))
  465. raise
  466. # Remove the primary key, the foreign keys will be removed by trigger
  467. self.base.delete(dbif)
  468. self.reset(None)
  469. if connect == True:
  470. dbif.close()
  471. def register_map(self, map, dbif=None):
  472. """Register a map in the space time dataset.
  473. This method takes care of the registration of a map
  474. in a space time dataset.
  475. In case the map is already registered this function will break with a warning
  476. and return False
  477. """
  478. connect = False
  479. if dbif == None:
  480. dbif = sql_database_interface()
  481. dbif.connect()
  482. connect = True
  483. if map.is_in_db(dbif) == False:
  484. core.fatal(_("Only maps with absolute or relative valid time can be registered"))
  485. core.verbose(_("Register %s map <%s> in space time %s dataset <%s>") % (map.get_type(), map.get_id(), map.get_type(), self.get_id()))
  486. # First select all data from the database
  487. map.select(dbif)
  488. map_id = map.base.get_id()
  489. map_name = map.base.get_name()
  490. map_mapset = map.base.get_mapset()
  491. map_register_table = map.get_stds_register()
  492. #print "Map register table", map_register_table
  493. # Get basic info
  494. stds_name = self.base.get_name()
  495. stds_mapset = self.base.get_mapset()
  496. stds_register_table = self.get_map_register()
  497. #print "STDS register table", stds_register_table
  498. if stds_mapset != map_mapset:
  499. core.fatal(_("Only maps from the same mapset can be registered"))
  500. # Check if map is already registred
  501. if stds_register_table:
  502. sql = "SELECT id FROM " + stds_register_table + " WHERE id = (?)"
  503. dbif.cursor.execute(sql, (map_id,))
  504. row = dbif.cursor.fetchone()
  505. # In case of no entry make a new one
  506. if row and row[0] == map_id:
  507. core.warning(_("Map <%s> is already registered.") % (map_id))
  508. return False
  509. # Create tables
  510. sql_path = get_sql_template_path()
  511. # We need to create the stmap raster register table bevor we can register the map
  512. if map_register_table == None:
  513. # Create a unique id
  514. uuid_rand = "map_" + str(uuid.uuid4()).replace("-", "")
  515. map_register_table = uuid_rand + "_" + self.get_type() + "_register"
  516. # Read the SQL template
  517. sql = open(os.path.join(sql_path, "map_stds_register_table_template.sql"), 'r').read()
  518. # Create the raster, raster3d and vector tables
  519. sql = sql.replace("GRASS_MAP", map.get_type())
  520. sql = sql.replace("MAP_NAME", map_name + "_" + map_mapset )
  521. sql = sql.replace("TABLE_NAME", uuid_rand )
  522. sql = sql.replace("MAP_ID", map_id)
  523. sql = sql.replace("STDS", self.get_type())
  524. try:
  525. dbif.cursor.executescript(sql)
  526. except:
  527. try:
  528. # Drop stds register table
  529. sql = "DROP TABLE " + map_register_table
  530. dbif.cursor.execute(sql)
  531. except:
  532. core.error(_("Unable to drop table <%s>" % (map_register_table)))
  533. raise
  534. try:
  535. dbif.cursor.executescript(sql_script)
  536. except:
  537. core.error(_("Unable to create the space time %s dataset register table for <%s>") % (map.get_type(), map.get_id()))
  538. raise
  539. # Set the stds register table name and put it into the DB
  540. map.set_stds_register(map_register_table)
  541. map.metadata.update(dbif)
  542. core.verbose(_("Created register table <%s> for %s map <%s>") % (map_register_table, map.get_type(), map.get_id()))
  543. # We need to create the table and register it
  544. if stds_register_table == None:
  545. # Create table name
  546. stds_register_table = stds_name + "_" + stds_mapset + "_" + map.get_type() + "_register"
  547. # Read the SQL template
  548. sql = open(os.path.join(sql_path, "stds_map_register_table_template.sql"), 'r').read()
  549. # Create the raster, raster3d and vector tables
  550. sql = sql.replace("GRASS_MAP", map.get_type())
  551. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  552. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  553. sql = sql.replace("STDS", self.get_type())
  554. sql_script = ""
  555. #sql_script += "BEGIN TRANSACTION;\n"
  556. sql_script += sql
  557. #sql_script += "\n"
  558. #sql_script += "END TRANSACTION;"
  559. try:
  560. dbif.cursor.executescript(sql_script)
  561. except:
  562. try:
  563. # Drop map register table
  564. sql = "DROP TABLE " + stds_register_table
  565. dbif.cursor.execute(sql)
  566. except:
  567. core.error(_("Unable to drop table <%s>" % (stds_register_table)))
  568. raise
  569. try:
  570. dbif.cursor.executescript(sql_script)
  571. except:
  572. core.error(_("Unable to create the space time %s dataset register table for <%s>") % (map.get_type(), map.get_id()))
  573. raise
  574. # Trigger have been disabled due to peformance issues while registration
  575. ## We need raster specific trigger
  576. #sql = open(os.path.join(sql_path, "stds_" + map.get_type() + "_register_trigger_template.sql"), 'r').read()
  577. #sql = sql.replace("GRASS_MAP", map.get_type())
  578. #sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  579. #sql = sql.replace("SPACETIME_ID", self.base.get_id())
  580. #sql = sql.replace("STDS", self.get_type())
  581. #sql_script = ""
  582. #sql_script += "BEGIN TRANSACTION;\n"
  583. #sql_script += sql
  584. #sql_script += "\n"
  585. #sql_script += "END TRANSACTION;"
  586. #dbif.cursor.executescript(sql_script)
  587. # Set the map register table name and put it into the DB
  588. self.set_map_register(stds_register_table)
  589. self.metadata.update(dbif)
  590. core.verbose(_("Created register table <%s> for space time %s dataset <%s>") % (stds_register_table, map.get_type(), self.get_id()))
  591. # Register the stds in the map stds register table
  592. # Check if the entry is already there
  593. sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
  594. dbif.cursor.execute(sql, (self.base.get_id(),))
  595. row = dbif.cursor.fetchone()
  596. # In case of no entry make a new one
  597. if row == None:
  598. sql = "INSERT INTO " + map_register_table + " (id) " + "VALUES (?)"
  599. #print sql
  600. dbif.cursor.execute(sql, (self.base.get_id(),))
  601. # Now put the raster name in the stds map register table
  602. sql = "INSERT INTO " + stds_register_table + " (id) " + "VALUES (?)"
  603. #print sql
  604. dbif.cursor.execute(sql, (map_id,))
  605. if connect == True:
  606. dbif.close()
  607. return True
  608. def unregister_map(self, map, dbif = None):
  609. """Unregister a map from the space time dataset.
  610. This method takes care of the unregistration of a map
  611. from a space time dataset.
  612. """
  613. connect = False
  614. if dbif == None:
  615. dbif = sql_database_interface()
  616. dbif.connect()
  617. connect = True
  618. if map.is_in_db(dbif) == False:
  619. core.fatal(_("Unable to find map <%s> in temporal database") % (map.get_id()))
  620. core.verbose(_("Unregister %s map <%s>") % (map.get_type(), map.get_id()))
  621. # First select all data from the database
  622. map.select(dbif)
  623. map_id = map.base.get_id()
  624. map_register_table = map.get_stds_register()
  625. # Get basic info
  626. stds_register_table = self.get_map_register()
  627. # Check if the map is registered in the space time raster dataset
  628. sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
  629. dbif.cursor.execute(sql, (self.base.get_id(),))
  630. row = dbif.cursor.fetchone()
  631. # Break if the map is not registered
  632. if row == None:
  633. core.warning(_("Map <%s> is not registered in space time dataset") %(map_id, self.base.get_id()))
  634. return False
  635. # Remove the space time raster dataset from the raster dataset register
  636. if map_register_table != None:
  637. sql = "DELETE FROM " + map_register_table + " WHERE id = ?"
  638. dbif.cursor.execute(sql, (self.base.get_id(),))
  639. # Remove the raster map from the space time raster dataset register
  640. if stds_register_table != None:
  641. sql = "DELETE FROM " + stds_register_table + " WHERE id = ?"
  642. dbif.cursor.execute(sql, (map_id,))
  643. if connect == True:
  644. dbif.close()
  645. def update_from_registered_maps(self, dbif = None):
  646. """This methods updates the spatial and temporal extent as well as
  647. type specific metadata. It should always been called after maps are registered
  648. or unregistered/deleted from the space time dataset.
  649. The update of the temporal extent checks if the end time is set correctly.
  650. In case the registered maps have no valid end time (None) the maximum start time
  651. will be used. If the end time is smaller than the maximum start time, it will
  652. be replaced by the maximum start time.
  653. An other solution to automate this is to use the diactivated trigger
  654. in the SQL files. But this will result in a huge performance issue
  655. in case many maps are registred (>1000).
  656. """
  657. core.verbose(_("Update metadata, spatial and temporal extent from all registered maps of <%s>") % (self.get_id()))
  658. connect = False
  659. if dbif == None:
  660. dbif = sql_database_interface()
  661. dbif.connect()
  662. connect = True
  663. use_start_time = False
  664. # Get basic info
  665. stds_name = self.base.get_name()
  666. stds_mapset = self.base.get_mapset()
  667. sql_path = get_sql_template_path()
  668. #We create a transaction
  669. sql_script = ""
  670. sql_script += "BEGIN TRANSACTION;\n"
  671. # Update the spatial and temporal extent from registered maps
  672. # Read the SQL template
  673. sql = open(os.path.join(sql_path, "update_stds_spatial_temporal_extent_template.sql"), 'r').read()
  674. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  675. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  676. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  677. sql = sql.replace("STDS", self.get_type())
  678. sql_script += sql
  679. sql_script += "\n"
  680. # Update type specific metadata
  681. sql = open(os.path.join(sql_path, "update_" + self.get_type() + "_metadata_template.sql"), 'r').read()
  682. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  683. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  684. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  685. sql = sql.replace("STDS", self.get_type())
  686. sql_script += sql
  687. sql_script += "\n"
  688. sql_script += "END TRANSACTION;"
  689. dbif.cursor.executescript(sql_script)
  690. # Read and validate the selected end time
  691. self.select()
  692. if self.is_time_absolute():
  693. start_time, end_time, tz = self.get_absolute_time()
  694. else:
  695. start_time, end_time = self.get_relative_time()
  696. # In case no end time is set, use the maximum start time of all registered maps as end time
  697. if end_time == None:
  698. use_start_time = True
  699. else:
  700. # Check if the end time is smaller than the maximum start time
  701. if self.is_time_absolute():
  702. sql = """SELECT max(start_time) FROM GRASS_MAP_absolute_time WHERE GRASS_MAP_absolute_time.id IN
  703. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register);"""
  704. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  705. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  706. else:
  707. sql = """SELECT max(start_time) FROM GRASS_MAP_relative_time WHERE GRASS_MAP_relative_time.id IN
  708. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register);"""
  709. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  710. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  711. dbif.cursor.execute(sql)
  712. row = dbif.cursor.fetchone()
  713. if row != None:
  714. tstring = row[0]
  715. # Convert the unicode string into the datetime format
  716. if tstring.find(":") > 0:
  717. time_format = "%Y-%m-%d %H:%M:%S"
  718. else:
  719. time_format = "%Y-%m-%d"
  720. max_start_time = datetime.strptime(tstring, time_format)
  721. if end_time < max_start_time:
  722. use_start_time = True
  723. # Set the maximum start time as end time
  724. if use_start_time:
  725. if self.is_time_absolute():
  726. sql = """UPDATE STDS_absolute_time SET end_time =
  727. (SELECT max(start_time) FROM GRASS_MAP_absolute_time WHERE GRASS_MAP_absolute_time.id IN
  728. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
  729. ) WHERE id = "SPACETIME_ID";"""
  730. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  731. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  732. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  733. sql = sql.replace("STDS", self.get_type())
  734. elif self.is_time_relative():
  735. sql = """UPDATE STDS_relative_time SET end_time =
  736. (SELECT max(start_time) FROM GRASS_MAP_relative_time WHERE GRASS_MAP_relative_time.id IN
  737. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
  738. ) WHERE id = "SPACETIME_ID";"""
  739. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  740. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  741. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  742. sql = sql.replace("STDS", self.get_type())
  743. dbif.cursor.executescript(sql)
  744. if connect == True:
  745. dbif.close()