abstract_space_time_dataset.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. """!@package grass.temporal
  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. import grass.temporal as tgis
  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. from abstract_dataset import *
  16. ###############################################################################
  17. class abstract_space_time_dataset(abstract_dataset):
  18. """Abstract space time dataset class
  19. This class represents a space time dataset. Convenient functions
  20. to select, update, insert or delete objects of this type in the SQL
  21. temporal database exists as well as functions to register or unregister
  22. raster maps.
  23. Parts of the temporal logic are implemented in the SQL temporal database,
  24. like the computation of the temporal and spatial extent as well as the
  25. collecting of metadata.
  26. """
  27. def __init__(self, ident):
  28. self.reset(ident)
  29. def get_new_instance(self, ident=None):
  30. """Return a new instance with the type of this class
  31. @param ident: The unique identifier of the new object
  32. """
  33. raise IOError("This method must be implemented in the subclasses")
  34. def get_new_map_instance(self, ident=None):
  35. """Return a new instance of a map dataset which is associated with the type of this class
  36. @param ident: The unique identifier of the new object
  37. """
  38. raise IOError("This method must be implemented in the subclasses")
  39. def get_map_register(self):
  40. """Return the name of the map register table"""
  41. raise IOError("This method must be implemented in the subclasses")
  42. def set_map_register(self, name):
  43. """Set the name of the map register table
  44. This table stores all map names which are registered in this space time dataset.
  45. @param name: The name of the register table
  46. """
  47. raise IOError("This method must be implemented in the subclasses")
  48. def set_initial_values(self, granularity, temporal_type, semantic_type, \
  49. title=None, description=None):
  50. """Set the initial values of the space time dataset
  51. @param granularity: The temporal granularity of this dataset. This value
  52. should be computed by the space time dataset itself,
  53. based on the granularity of the registered maps
  54. @param temporal_type: The temporal type of this space time dataset (absolute or relative)
  55. @param semantic_type: The semantic type of this dataset
  56. @param title: The title
  57. @param description: The description of this dataset
  58. """
  59. if temporal_type == "absolute":
  60. self.set_time_to_absolute()
  61. self.absolute_time.set_granularity(granularity)
  62. elif temporal_type == "relative":
  63. self.set_time_to_relative()
  64. self.relative_time.set_granularity(granularity)
  65. else:
  66. core.fatal(_("Unknown temporal type \"%s\"") % (temporal_type))
  67. self.base.set_semantic_type(semantic_type)
  68. self.metadata.set_title(title)
  69. self.metadata.set_description(description)
  70. def get_initial_values(self):
  71. """Return the initial values: granularity, temporal_type, semantic_type, title, description"""
  72. temporal_type = self.get_temporal_type()
  73. if temporal_type == "absolute":
  74. granularity = self.absolute_time.get_granularity()
  75. elif temporal_type == "relative":
  76. granularity = self.relative_time.get_granularity()
  77. semantic_type = self.base.get_semantic_type()
  78. title = self.metadata.get_title()
  79. description = self.metadata.get_description()
  80. return granularity, temporal_type, semantic_type, title, description
  81. def print_temporal_relation_matrix(self, maps):
  82. """Print the temporal relation matrix of all registered maps to stdout
  83. The temproal relation matrix includes the temporal relations between
  84. all registered maps. The relations are strings stored in a list of lists.
  85. @param dbif: The database interface to be used
  86. """
  87. for map in maps:
  88. print map.get_id(),
  89. print " "
  90. for mapA in maps:
  91. for mapB in maps:
  92. print mapA.temporal_relation(mapB),
  93. print " "
  94. def get_temporal_relation_matrix(self, maps):
  95. """Return the temporal relation matrix of all registered maps as listof lists
  96. The map list must be ordered by start time
  97. The temproal relation matrix includes the temporal relations between
  98. all registered maps. The relations are strings stored in a list of lists.
  99. @param dbif: The database interface to be used
  100. """
  101. matrix = []
  102. # Create the temporal relation matrix
  103. # Add the map names first
  104. row = []
  105. for map in maps:
  106. row.append(map.get_id())
  107. matrix.append(row)
  108. for mapA in maps:
  109. row = []
  110. for mapB in maps:
  111. row.append(mapA.temporal_relation(mapB))
  112. matrix.append(row)
  113. return matrix
  114. def get_temporal_map_type_count(self, maps):
  115. """Return the temporal type of the registered maps as dictionary
  116. The map list must be ordered by start time
  117. The temporal type can be:
  118. * point -> only the start time is present
  119. * interval -> start and end time
  120. * invalid -> No valid time point or interval found
  121. * holes -> In case the maps are interval
  122. @param dbif: The database interface to be used
  123. """
  124. time_invalid = 0
  125. time_point = 0
  126. time_interval = 0
  127. tcount = {}
  128. for i in range(len(maps)):
  129. # Check for point and interval data
  130. if maps[i].is_time_absolute():
  131. start, end, tz = maps[i].get_absolute_time()
  132. if maps[i].is_time_relative():
  133. start, end = maps[i].get_relative_time()
  134. if start and end:
  135. time_interval += 1
  136. # Check for holes
  137. if i < len(maps) - 1:
  138. relation = maps[i + 1].temporal_relation(maps[i])
  139. if relation != "follows":
  140. holes += 1
  141. elif start and not end:
  142. time_point += 1
  143. else:
  144. time_invalid += 1
  145. tcount["point"] = time_point
  146. tcount["interval"] = time_interval
  147. tcount["invalid"] = time_invalid
  148. holes = 0
  149. # Check for holes
  150. if time_interval > 0 and time_point == 0 and time_invalid == 0:
  151. for i in range(len(maps)):
  152. if i < len(maps) - 1:
  153. relation = maps[i + 1].temporal_relation(maps[i])
  154. if relation != "follows":
  155. holes += 1
  156. tcount["holes"] = holes
  157. return tcount
  158. def get_temporal_relations_count(self, maps):
  159. """Count the temporal relations between the registered maps.
  160. The map list must be ordered by start time
  161. @param dbif: The database interface to be used
  162. """
  163. tcount = {}
  164. for i in range(len(maps)):
  165. # Check for point and interval data
  166. for j in range(i + 1, len(maps)):
  167. relation = maps[j].temporal_relation(maps[i])
  168. if tcount.has_key(relation):
  169. tcount[relation] = tcount[relation] + 1
  170. else:
  171. tcount[relation] = 1
  172. return tcount
  173. def get_registered_maps_as_objects(self, where=None, order="start_time", dbif=None):
  174. """Return all registered maps as ordered object list
  175. @param where: The SQL where statement to select a subset of the registered maps without "WHERE"
  176. @param order: The SQL order statement to be used to order the objects in the list without "ORDER BY"
  177. @param dbif: The database interface to be used
  178. In case nothing found None is returned
  179. """
  180. connect = False
  181. if dbif == None:
  182. dbif = sql_database_interface()
  183. dbif.connect()
  184. connect = True
  185. obj_list = []
  186. rows = self.get_registered_maps("id", where, order, dbif)
  187. if rows:
  188. for row in rows:
  189. map = self.get_new_map_instance(row["id"])
  190. map.select(dbif)
  191. obj_list.append(copy.copy(map))
  192. if connect == True:
  193. dbif.close()
  194. return obj_list
  195. def get_registered_maps(self, columns=None, where = None, order = None, dbif=None):
  196. """Return sqlite rows of all registered maps.
  197. Each row includes all columns specified in the datatype specific view
  198. @param columns: Columns to be selected as SQL compliant string
  199. @param where: The SQL where statement to select a subset of the registered maps without "WHERE"
  200. @param order: The SQL order statement to be used to order the objects in the list without "ORDER BY"
  201. @param dbif: The database interface to be used
  202. In case nothing found None is returned
  203. """
  204. connect = False
  205. if dbif == None:
  206. dbif = sql_database_interface()
  207. dbif.connect()
  208. connect = True
  209. rows = None
  210. if self.get_map_register():
  211. # Use the correct temporal table
  212. if self.get_temporal_type() == "absolute":
  213. map_view = self.get_new_map_instance(None).get_type() + "_view_abs_time"
  214. else:
  215. map_view = self.get_new_map_instance(None).get_type() + "_view_rel_time"
  216. if columns:
  217. sql = "SELECT %s FROM %s WHERE %s.id IN (SELECT id FROM %s)" % (columns, map_view, map_view, self.get_map_register())
  218. else:
  219. sql = "SELECT * FROM %s WHERE %s.id IN (SELECT id FROM %s)" % (map_view, map_view, self.get_map_register())
  220. if where:
  221. sql += " AND %s" % (where)
  222. if order:
  223. sql += " ORDER BY %s" % (order)
  224. try:
  225. dbif.cursor.execute(sql)
  226. rows = dbif.cursor.fetchall()
  227. except:
  228. if connect == True:
  229. dbif.close()
  230. core.error(_("Unable to get map ids from register table <%s>") % (self.get_map_register()))
  231. raise
  232. if connect == True:
  233. dbif.close()
  234. return rows
  235. def delete(self, dbif=None):
  236. """Delete a space time dataset from the temporal database
  237. This method removes the space time dataset from the temporal database and drops its map register table
  238. @param dbif: The database interface to be used
  239. """
  240. # First we need to check if maps are registered in this dataset and
  241. # unregister them
  242. core.verbose(_("Delete space time %s dataset <%s> from temporal database") % (self.get_new_map_instance(ident=None).get_type(), self.get_id()))
  243. connect = False
  244. if dbif == None:
  245. dbif = sql_database_interface()
  246. dbif.connect()
  247. connect = True
  248. # SELECT all needed informations from the database
  249. self.select(dbif)
  250. core.verbose(_("Drop map register table: %s") % (self.get_map_register()))
  251. if self.get_map_register():
  252. rows = self.get_registered_maps("id", None, None, dbif)
  253. # Unregister each registered map in the table
  254. if rows:
  255. for row in rows:
  256. # Unregister map
  257. map = self.get_new_map_instance(row["id"])
  258. self.unregister_map(map, dbif)
  259. try:
  260. # Drop the map register table
  261. sql = "DROP TABLE " + self.get_map_register()
  262. dbif.cursor.execute(sql)
  263. dbif.connection.commit()
  264. except:
  265. if connect == True:
  266. dbif.close()
  267. core.error(_("Unable to drop table <%s>") % (self.get_map_register()))
  268. raise
  269. # Remove the primary key, the foreign keys will be removed by trigger
  270. self.base.delete(dbif)
  271. self.reset(None)
  272. if connect == True:
  273. dbif.close()
  274. def register_map(self, map, dbif=None):
  275. """ Register a map in the space time dataset.
  276. This method takes care of the registration of a map
  277. in a space time dataset.
  278. In case the map is already registered this function will break with a warning
  279. and return False
  280. @param dbif: The database interface to be used
  281. """
  282. connect = False
  283. if dbif == None:
  284. dbif = sql_database_interface()
  285. dbif.connect()
  286. connect = True
  287. if map.is_in_db(dbif) == False:
  288. dbif.close()
  289. core.fatal(_("Only maps with absolute or relative valid time can be registered"))
  290. core.verbose(_("Register %s map <%s> in space time %s dataset <%s>") % (map.get_type(), map.get_id(), map.get_type(), self.get_id()))
  291. # First select all data from the database
  292. map.select(dbif)
  293. map_id = map.base.get_id()
  294. map_name = map.base.get_name()
  295. map_mapset = map.base.get_mapset()
  296. map_register_table = map.get_stds_register()
  297. #print "Map register table", map_register_table
  298. # Get basic info
  299. stds_name = self.base.get_name()
  300. stds_mapset = self.base.get_mapset()
  301. stds_register_table = self.get_map_register()
  302. #print "STDS register table", stds_register_table
  303. if stds_mapset != map_mapset:
  304. dbif.close()
  305. core.fatal(_("Only maps from the same mapset can be registered"))
  306. # Check if map is already registred
  307. if stds_register_table:
  308. if dbmi.paramstyle == "qmark":
  309. sql = "SELECT id FROM " + stds_register_table + " WHERE id = (?)"
  310. else:
  311. sql = "SELECT id FROM " + stds_register_table + " WHERE id = (%s)"
  312. dbif.cursor.execute(sql, (map_id,))
  313. row = dbif.cursor.fetchone()
  314. # In case of no entry make a new one
  315. if row and row[0] == map_id:
  316. if connect == True:
  317. dbif.close()
  318. core.warning(_("Map <%s> is already registered.") % (map_id))
  319. return False
  320. # Create tables
  321. sql_path = get_sql_template_path()
  322. # We need to create the stmap raster register table bevor we can register the map
  323. if map_register_table == None:
  324. # Create a unique id
  325. uuid_rand = "map_" + str(uuid.uuid4()).replace("-", "")
  326. map_register_table = uuid_rand + "_" + self.get_type() + "_register"
  327. # Read the SQL template
  328. sql = open(os.path.join(sql_path, "map_stds_register_table_template.sql"), 'r').read()
  329. # Create the raster, raster3d and vector tables
  330. sql = sql.replace("GRASS_MAP", map.get_type())
  331. sql = sql.replace("MAP_NAME", map_name + "_" + map_mapset )
  332. sql = sql.replace("TABLE_NAME", uuid_rand )
  333. sql = sql.replace("MAP_ID", map_id)
  334. sql = sql.replace("STDS", self.get_type())
  335. try:
  336. if dbmi.__name__ == "sqlite3":
  337. dbif.cursor.executescript(sql)
  338. else:
  339. dbif.cursor.execute(sql)
  340. except:
  341. if connect == True:
  342. dbif.close()
  343. core.error(_("Unable to create the space time %s dataset register table for <%s>") % \
  344. (map.get_type(), map.get_id()))
  345. raise
  346. # Set the stds register table name and put it into the DB
  347. map.set_stds_register(map_register_table)
  348. map.metadata.update(dbif)
  349. core.verbose(_("Created register table <%s> for %s map <%s>") % \
  350. (map_register_table, map.get_type(), map.get_id()))
  351. # We need to create the table and register it
  352. if stds_register_table == None:
  353. # Create table name
  354. stds_register_table = stds_name + "_" + stds_mapset + "_" + map.get_type() + "_register"
  355. # Read the SQL template
  356. sql = open(os.path.join(sql_path, "stds_map_register_table_template.sql"), 'r').read()
  357. # Create the raster, raster3d and vector tables
  358. sql = sql.replace("GRASS_MAP", map.get_type())
  359. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  360. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  361. sql = sql.replace("STDS", self.get_type())
  362. sql_script = ""
  363. sql_script += "BEGIN TRANSACTION;\n"
  364. sql_script += sql
  365. sql_script += "\n"
  366. sql_script += "END TRANSACTION;"
  367. try:
  368. if dbmi.__name__ == "sqlite3":
  369. dbif.cursor.executescript(sql_script)
  370. else:
  371. dbif.cursor.execute(sql_script)
  372. dbif.connection.commit()
  373. except:
  374. if connect == True:
  375. dbif.close()
  376. core.error(_("Unable to create the space time %s dataset register table for <%s>") % \
  377. (map.get_type(), map.get_id()))
  378. raise
  379. # Set the map register table name and put it into the DB
  380. self.set_map_register(stds_register_table)
  381. self.metadata.update(dbif)
  382. core.verbose(_("Created register table <%s> for space time %s dataset <%s>") % \
  383. (stds_register_table, map.get_type(), self.get_id()))
  384. # Register the stds in the map stds register table
  385. # Check if the entry is already there
  386. if dbmi.paramstyle == "qmark":
  387. sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
  388. else:
  389. sql = "SELECT id FROM " + map_register_table + " WHERE id = %s"
  390. dbif.cursor.execute(sql, (self.base.get_id(),))
  391. row = dbif.cursor.fetchone()
  392. # In case of no entry make a new one
  393. if row == None:
  394. if dbmi.paramstyle == "qmark":
  395. sql = "INSERT INTO " + map_register_table + " (id) " + "VALUES (?)"
  396. else:
  397. sql = "INSERT INTO " + map_register_table + " (id) " + "VALUES (%s)"
  398. #print sql
  399. dbif.cursor.execute(sql, (self.base.get_id(),))
  400. # Now put the raster name in the stds map register table
  401. if dbmi.paramstyle == "qmark":
  402. sql = "INSERT INTO " + stds_register_table + " (id) " + "VALUES (?)"
  403. else:
  404. sql = "INSERT INTO " + stds_register_table + " (id) " + "VALUES (%s)"
  405. #print sql
  406. dbif.cursor.execute(sql, (map_id,))
  407. if connect == True:
  408. dbif.close()
  409. return True
  410. def unregister_map(self, map, dbif = None):
  411. """Unregister a map from the space time dataset.
  412. This method takes care of the unregistration of a map
  413. from a space time dataset.
  414. @param map: The map object to unregister
  415. @param dbif: The database interface to be used
  416. """
  417. connect = False
  418. if dbif == None:
  419. dbif = sql_database_interface()
  420. dbif.connect()
  421. connect = True
  422. if map.is_in_db(dbif) == False:
  423. dbif.close()
  424. core.fatal(_("Unable to find map <%s> in temporal database") % (map.get_id()))
  425. core.verbose(_("Unregister %s map <%s>") % (map.get_type(), map.get_id()))
  426. # First select all data from the database
  427. map.select(dbif)
  428. map_id = map.base.get_id()
  429. map_register_table = map.get_stds_register()
  430. stds_register_table = self.get_map_register()
  431. # Check if the map is registered in the space time raster dataset
  432. if dbmi.paramstyle == "qmark":
  433. sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
  434. else:
  435. sql = "SELECT id FROM " + map_register_table + " WHERE id = %s"
  436. dbif.cursor.execute(sql, (self.base.get_id(),))
  437. row = dbif.cursor.fetchone()
  438. # Break if the map is not registered
  439. if row == None:
  440. core.warning(_("Map <%s> is not registered in space time dataset") %(map_id, self.base.get_id()))
  441. if connect == True:
  442. dbif.close()
  443. return False
  444. # Remove the space time raster dataset from the raster dataset register
  445. if map_register_table != None:
  446. if dbmi.paramstyle == "qmark":
  447. sql = "DELETE FROM " + map_register_table + " WHERE id = ?"
  448. else:
  449. sql = "DELETE FROM " + map_register_table + " WHERE id = %s"
  450. dbif.cursor.execute(sql, (self.base.get_id(),))
  451. # Remove the raster map from the space time raster dataset register
  452. if stds_register_table != None:
  453. if dbmi.paramstyle == "qmark":
  454. sql = "DELETE FROM " + stds_register_table + " WHERE id = ?"
  455. else:
  456. sql = "DELETE FROM " + stds_register_table + " WHERE id = %s"
  457. dbif.cursor.execute(sql, (map_id,))
  458. if connect == True:
  459. dbif.close()
  460. def update_from_registered_maps(self, dbif = None):
  461. """This methods updates the spatial and temporal extent as well as
  462. type specific metadata. It should always been called after maps are registered
  463. or unregistered/deleted from the space time dataset.
  464. The update of the temporal extent checks if the end time is set correctly.
  465. In case the registered maps have no valid end time (None) the maximum start time
  466. will be used. If the end time is earlier than the maximum start time, it will
  467. be replaced by the maximum start time.
  468. An other solution to automate this is to use the diactivated trigger
  469. in the SQL files. But this will result in a huge performance issue
  470. in case many maps are registred (>1000).
  471. @param dbif: The database interface to be used
  472. """
  473. core.verbose(_("Update metadata, spatial and temporal extent from all registered maps of <%s>") % (self.get_id()))
  474. # Nothing to do if the register is not present
  475. if not self.get_map_register():
  476. return
  477. connect = False
  478. if dbif == None:
  479. dbif = sql_database_interface()
  480. dbif.connect()
  481. connect = True
  482. map_time = None
  483. use_start_time = False
  484. # Get basic info
  485. stds_name = self.base.get_name()
  486. stds_mapset = self.base.get_mapset()
  487. sql_path = get_sql_template_path()
  488. #We create a transaction
  489. sql_script = ""
  490. sql_script += "BEGIN TRANSACTION;\n"
  491. # Update the spatial and temporal extent from registered maps
  492. # Read the SQL template
  493. sql = open(os.path.join(sql_path, "update_stds_spatial_temporal_extent_template.sql"), 'r').read()
  494. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  495. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  496. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  497. sql = sql.replace("STDS", self.get_type())
  498. sql_script += sql
  499. sql_script += "\n"
  500. # Update type specific metadata
  501. sql = open(os.path.join(sql_path, "update_" + self.get_type() + "_metadata_template.sql"), 'r').read()
  502. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  503. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  504. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  505. sql = sql.replace("STDS", self.get_type())
  506. sql_script += sql
  507. sql_script += "\n"
  508. sql_script += "END TRANSACTION;"
  509. if dbmi.__name__ == "sqlite3":
  510. dbif.cursor.executescript(sql_script)
  511. else:
  512. dbif.cursor.execute(sql_script)
  513. # Read and validate the selected end time
  514. self.select()
  515. if self.is_time_absolute():
  516. start_time, end_time, tz = self.get_absolute_time()
  517. else:
  518. start_time, end_time = self.get_relative_time()
  519. # In case no end time is set, use the maximum start time of all registered maps as end time
  520. if end_time == None:
  521. use_start_time = True
  522. else:
  523. # Check if the end time is smaller than the maximum start time
  524. if self.is_time_absolute():
  525. sql = """SELECT max(start_time) FROM GRASS_MAP_absolute_time WHERE GRASS_MAP_absolute_time.id IN
  526. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register);"""
  527. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  528. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  529. else:
  530. sql = """SELECT max(start_time) FROM GRASS_MAP_relative_time WHERE GRASS_MAP_relative_time.id IN
  531. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register);"""
  532. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  533. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  534. dbif.cursor.execute(sql)
  535. row = dbif.cursor.fetchone()
  536. if row != None:
  537. # This seems to be a bug in sqlite3 Python driver
  538. if dbmi.__name__ == "sqlite3":
  539. tstring = row[0]
  540. # Convert the unicode string into the datetime format
  541. if tstring.find(":") > 0:
  542. time_format = "%Y-%m-%d %H:%M:%S"
  543. else:
  544. time_format = "%Y-%m-%d"
  545. max_start_time = datetime.strptime(tstring, time_format)
  546. else:
  547. max_start_time = row[0]
  548. if end_time < max_start_time:
  549. map_time = "mixed"
  550. use_start_time = True
  551. else:
  552. map_time = "interval"
  553. # Set the maximum start time as end time
  554. if use_start_time:
  555. if self.is_time_absolute():
  556. sql = """UPDATE STDS_absolute_time SET end_time =
  557. (SELECT max(start_time) FROM GRASS_MAP_absolute_time WHERE GRASS_MAP_absolute_time.id IN
  558. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
  559. ) WHERE id = 'SPACETIME_ID';"""
  560. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  561. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  562. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  563. sql = sql.replace("STDS", self.get_type())
  564. elif self.is_time_relative():
  565. sql = """UPDATE STDS_relative_time SET end_time =
  566. (SELECT max(start_time) FROM GRASS_MAP_relative_time WHERE GRASS_MAP_relative_time.id IN
  567. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
  568. ) WHERE id = 'SPACETIME_ID';"""
  569. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  570. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  571. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  572. sql = sql.replace("STDS", self.get_type())
  573. if dbmi.__name__ == "sqlite3":
  574. dbif.cursor.executescript(sql)
  575. else:
  576. dbif.cursor.execute(sql)
  577. if end_time == None:
  578. map_time = "point"
  579. # Set the map time type
  580. if self.is_time_absolute():
  581. self.absolute_time.select(dbif)
  582. self.metadata.select(dbif)
  583. if self.metadata.get_number_of_maps() > 0:
  584. self.absolute_time.set_map_time(map_time)
  585. else:
  586. self.absolute_time.set_map_time(None)
  587. self.absolute_time.update_all(dbif)
  588. else:
  589. self.relative_time.select(dbif)
  590. self.metadata.select(dbif)
  591. if self.metadata.get_number_of_maps() > 0:
  592. self.relative_time.set_map_time(map_time)
  593. else:
  594. self.relative_time.set_map_time(None)
  595. self.relative_time.update_all(dbif)
  596. # TODO: Compute the granularity of the dataset and update the database entry
  597. if connect == True:
  598. dbif.close()