abstract_map_dataset.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  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. class temporal_map_relations(abstract_dataset):
  19. """!This class implements a temporal topology access structure
  20. This object will be set up by temporal topology creation methods.
  21. If correctly initialize the calls next() and prev() let the user walk temporally forward
  22. and backward in time.
  23. The following temporal relations with access methods are supported:
  24. * equal
  25. * follows
  26. * precedes
  27. * overlaps
  28. * overlapped
  29. * during (including starts, finishes)
  30. * contains (including started, finished)
  31. Code:
  32. # We have build the temporal topology and we know the first map
  33. start = first
  34. while start:
  35. # Print all maps this map temporally contains
  36. dlist = start.get_contains()
  37. for _map in dlist:
  38. _map.print_info()
  39. start = start.next()
  40. """
  41. def __init__(self):
  42. self.reset_temporal_topology()
  43. def reset_temporal_topology(self):
  44. """!Reset any information about temporal topology"""
  45. self._temporal_topology = {}
  46. self._has_temporal_topology = False
  47. def set_temporal_topology_build_true(self):
  48. """!Same as name"""
  49. self._has_temporal_topology = True
  50. def set_temporal_topology_build_false(self):
  51. """!Same as name"""
  52. self._has_temporal_topology = False
  53. def is_temporal_topology_build(self):
  54. """!Check if the temporal topology was build"""
  55. return self._has_temporal_topology
  56. def set_next(self, _map):
  57. """!Set the map that is temporally as closest located after this map.
  58. Temporally located means that the start time of the "next" map is
  59. temporally located AFTER the start time of this map, but temporally
  60. near than other maps of the same dataset.
  61. @param _map: This object should be of type abstract_map_dataset or derived classes
  62. """
  63. self._temporal_topology["NEXT"] = _map
  64. def set_prev(self, _map):
  65. """!Set the map that is temporally as closest located before this map.
  66. Temporally located means that the start time of the "previous" map is
  67. temporally located BEFORE the start time of this map, but temporally
  68. near than other maps of the same dataset.
  69. @param _map: This object should be of type abstract_map_dataset or derived classes
  70. """
  71. self._temporal_topology["PREV"] = _map
  72. def next(self):
  73. """!Return the map with a start time temporally located after
  74. the start time of this map, but temporal closer than other maps
  75. @return A map object or None
  76. """
  77. if not self._temporal_topology.has_key("NEXT"):
  78. return None
  79. return self._temporal_topology["NEXT"]
  80. def prev(self):
  81. """!Return the map with a start time temporally located before
  82. the start time of this map, but temporal closer than other maps
  83. @return A map object or None
  84. """
  85. if not self._temporal_topology.has_key("PREV"):
  86. return None
  87. return self._temporal_topology["PREV"]
  88. def append_equivalent(self, _map):
  89. """!Append a map with equivalent temporal extent as this map
  90. @param _map: This object should be of type abstract_map_dataset or derived classes
  91. """
  92. if not self._temporal_topology.has_key("EQUAL"):
  93. self._temporal_topology["EQUAL"] = []
  94. self._temporal_topology["EQUAL"].append(_map)
  95. def get_equivalent(self):
  96. """!Return a list of map objects with equivalent temporal extent as this map
  97. @return A list of map objects or None
  98. """
  99. if not self._temporal_topology.has_key("EQUAL"):
  100. return None
  101. return self._temporal_topology["EQUAL"]
  102. def append_overlaps(self, _map):
  103. """!Append a map that this map temporally overlaps
  104. @param _map: This object should be of type abstract_map_dataset or derived classes
  105. """
  106. if not self._temporal_topology.has_key("OVERLAPS"):
  107. self._temporal_topology["OVERLAPS"] = []
  108. self._temporal_topology["OVERLAPS"].append(_map)
  109. def get_overlaps(self):
  110. """!Return a list of map objects that this map temporally overlaps
  111. @return A list of map objects or None
  112. """
  113. if not self._temporal_topology.has_key("OVERLAPS"):
  114. return None
  115. return self._temporal_topology["OVERLAPS"]
  116. def append_overlapped(self, _map):
  117. """!Append a map that this map temporally overlapped
  118. @param _map: This object should be of type abstract_map_dataset or derived classes
  119. """
  120. if not self._temporal_topology.has_key("OVERLAPPED"):
  121. self._temporal_topology["OVERLAPPED"] = []
  122. self._temporal_topology["OVERLAPPED"].append(_map)
  123. def get_overlapped(self):
  124. """!Return a list of map objects that this map temporally overlapped
  125. @return A list of map objects or None
  126. """
  127. if not self._temporal_topology.has_key("OVERLAPPED"):
  128. return None
  129. return self._temporal_topology["OVERLAPPED"]
  130. def append_follows(self, _map):
  131. """!Append a map that this map temporally follows
  132. @param _map: This object should be of type abstract_map_dataset or derived classes
  133. """
  134. if not self._temporal_topology.has_key("FOLLOWS"):
  135. self._temporal_topology["FOLLOWS"] = []
  136. self._temporal_topology["FOLLOWS"].append(_map)
  137. def get_follows(self):
  138. """!Return a list of map objects that this map temporally follows
  139. @return A list of map objects or None
  140. """
  141. if not self._temporal_topology.has_key("FOLLOWS"):
  142. return None
  143. return self._temporal_topology["FOLLOWS"]
  144. def append_precedes(self, _map):
  145. """!Append a map that this map temporally precedes
  146. @param _map: This object should be of type abstract_map_dataset or derived classes
  147. """
  148. if not self._temporal_topology.has_key("PRECEDES"):
  149. self._temporal_topology["PRECEDES"] = []
  150. self._temporal_topology["PRECEDES"].append(_map)
  151. def get_precedes(self):
  152. """!Return a list of map objects that this map temporally precedes
  153. @return A list of map objects or None
  154. """
  155. if not self._temporal_topology.has_key("PRECEDES"):
  156. return None
  157. return self._temporal_topology["PRECEDES"]
  158. def append_during(self, _map):
  159. """!Append a map that this map is temporally located during
  160. This includes temporal relationships starts and finishes
  161. @param _map: This object should be of type abstract_map_dataset or derived classes
  162. """
  163. if not self._temporal_topology.has_key("DURING"):
  164. self._temporal_topology["DURING"] = []
  165. self._temporal_topology["DURING"].append(_map)
  166. def get_during(self):
  167. """!Return a list of map objects that this map is temporally located during
  168. This includes temporally relationships starts and finishes
  169. @return A list of map objects or None
  170. """
  171. if not self._temporal_topology.has_key("DURING"):
  172. return None
  173. return self._temporal_topology["DURING"]
  174. def append_contains(self, _map):
  175. """!Append a map that this map temporally contains
  176. This includes temporal relationships started and finished
  177. @param _map: This object should be of type abstract_map_dataset or derived classes
  178. """
  179. if not self._temporal_topology.has_key("CONTAINS"):
  180. self._temporal_topology["CONTAINS"] = []
  181. self._temporal_topology["CONTAINS"].append(_map)
  182. def get_contains(self):
  183. """!Return a list of map objects that this map temporally contains
  184. This includes temporal relationships started and finished
  185. @return A list of map objects or None
  186. """
  187. if not self._temporal_topology.has_key("CONTAINS"):
  188. return None
  189. return self._temporal_topology["CONTAINS"]
  190. def _generate_map_list_string(self, map_list, line_wrap = True):
  191. count = 0
  192. string = ""
  193. for _map in map_list:
  194. if line_wrap and count > 0 and count % 3 == 0:
  195. string += "\n | ............................ "
  196. count = 0
  197. if count == 0:
  198. string += _map.get_id()
  199. else:
  200. string += ",%s" % _map.get_id()
  201. count += 1
  202. return string
  203. def print_temporal_topology_info(self):
  204. """!Print information about this class in human shell style"""
  205. _next = self.next()
  206. _prev = self.prev()
  207. _equal = self.get_equivalent()
  208. _follows = self.get_follows()
  209. _precedes = self.get_precedes()
  210. _overlaps = self.get_overlaps()
  211. _overlapped = self.get_overlapped()
  212. _during = self.get_during()
  213. _contains = self.get_contains()
  214. print " +-------------------- Temporal Topology -------------------------------------+"
  215. # 0123456789012345678901234567890
  216. if _next:
  217. print " | Next: ...................... " + str(_next.get_id())
  218. if _prev:
  219. print " | Previous: .................. " + str(_prev.get_id())
  220. if _equal:
  221. print " | Equivalent: ................ " + self._generate_map_list_string(_equal)
  222. if _follows:
  223. print " | Follows: ................... " + self._generate_map_list_string(_follows)
  224. if _precedes:
  225. print " | Precedes: .................. " + self._generate_map_list_string(_precedes)
  226. if _overlaps:
  227. print " | Overlaps: .................. " + self._generate_map_list_string(_overlaps)
  228. if _overlapped:
  229. print " | Overlapped: ................ " + self._generate_map_list_string(_overlapped)
  230. if _during:
  231. print " | During: .................... " + self._generate_map_list_string(_during)
  232. if _contains:
  233. print " | Contains: .................. " + self._generate_map_list_string(_contains)
  234. def print_temporal_topology_shell_info(self):
  235. """!Print information about this class in human readable style"""
  236. _next = self.next()
  237. _prev = self.prev()
  238. _equal = self.get_equivalent()
  239. _follows = self.get_follows()
  240. _precedes = self.get_precedes()
  241. _overlaps = self.get_overlaps()
  242. _overlapped = self.get_overlapped()
  243. _during = self.get_during()
  244. _contains = self.get_contains()
  245. if _next:
  246. print "next=" + _next.get_id()
  247. if _prev:
  248. print "prev=" + _prev.get_id()
  249. if _equal:
  250. print "equivalent=" +self._generate_map_list_string(_equal, False)
  251. if _follows:
  252. print "follows=" +self._generate_map_list_string(_follows, False)
  253. if _precedes:
  254. print "precedes=" +self._generate_map_list_string(_precedes, False)
  255. if _overlaps:
  256. print "overlaps=" +self._generate_map_list_string(_overlaps, False)
  257. if _overlapped:
  258. print "overlapped=" +self._generate_map_list_string(_overlapped, False)
  259. if _during:
  260. print "during=" +self._generate_map_list_string(_during, False)
  261. if _contains:
  262. print "contains=" +self._generate_map_list_string(_contains, False)
  263. ###############################################################################
  264. class abstract_map_dataset(temporal_map_relations):
  265. """!This is the base class for all maps (raster, vector, raster3d)
  266. providing additional function to set the valid time and the spatial extent.
  267. """
  268. def __init__(self):
  269. temporal_map_relations.__init__(self)
  270. def get_new_stds_instance(self, ident):
  271. """!Return a new space time dataset instance in which maps are stored with the type of this class
  272. @param ident: The identifier of the dataset
  273. """
  274. raise IOError("This method must be implemented in the subclasses")
  275. def get_stds_register(self):
  276. """!Return the space time dataset register table name in which stds are listed in which this map is registered"""
  277. raise IOError("This method must be implemented in the subclasses")
  278. def set_stds_register(self, name):
  279. """!Set the space time dataset register table name.
  280. This table stores all space time datasets in which this map is registered.
  281. @param ident: The name of the register table
  282. """
  283. raise IOError("This method must be implemented in the subclasses")
  284. def check_resolution_with_current_region(self):
  285. """!Check if the raster or voxel resolution is finer than the current resolution
  286. Return "finer" in case the raster/voxel resolution is finer than the current region
  287. Return "coarser" in case the raster/voxel resolution is coarser than the current region
  288. Vector maps are alwyas finer than the current region
  289. """
  290. raise IOError("This method must be implemented in the subclasses")
  291. def has_grass_timestamp(self):
  292. """!Check if a grass file bsased time stamp exists for this map.
  293. """
  294. raise IOError("This method must be implemented in the subclasses")
  295. def write_timestamp_to_grass(self):
  296. """!Write the timestamp of this map into the map metadata in the grass file system based spatial
  297. database.
  298. """
  299. raise IOError("This method must be implemented in the subclasses")
  300. def remove_timestamp_from_grass(self):
  301. """!Remove the timestamp from the grass file system based spatial database
  302. """
  303. raise IOError("This method must be implemented in the subclasses")
  304. def map_exists(self):
  305. """!Return True in case the map exists in the grass spatial database
  306. @return True if map exists, False otherwise
  307. """
  308. raise IOError("This method must be implemented in the subclasses")
  309. def read_info(self):
  310. """!Read the map info from the grass file system based database and store the content
  311. into a dictionary
  312. """
  313. raise IOError("This method must be implemented in the subclasses")
  314. def load(self):
  315. """!Load the content of this object from the grass file system based database"""
  316. raise IOError("This method must be implemented in the subclasses")
  317. def _convert_timestamp(self):
  318. """!Convert the valid time into a grass datetime library compatible timestamp string
  319. This methods works for reltaive and absolute time
  320. @return the grass timestamp string
  321. """
  322. start = ""
  323. if self.is_time_absolute():
  324. start_time, end_time, tz = self.get_absolute_time()
  325. start = datetime_to_grass_datetime_string(start_time)
  326. if end_time:
  327. end = datetime_to_grass_datetime_string(end_time)
  328. start += " / %s"%(end)
  329. else:
  330. start_time, end_time, unit = self.get_relative_time()
  331. start = "%i %s"%(int(start_time), unit)
  332. if end_time != None:
  333. end = "%i %s"%(int(end_time), unit)
  334. start += " / %s"%(end)
  335. return start
  336. def get_map_id(self):
  337. """!Return the map id. The map id is the unique map identifier in grass and must not be equal to the
  338. primary key identifier (id) of the map in the database. Since vector maps may have layer information,
  339. the unique id is a combination of name, layer and mapset.
  340. Use get_map_id() every time your need to access the grass map in the file system but not to identify
  341. map information in the temporal database.
  342. """
  343. return self.base.get_map_id()
  344. def build_id(self, name, mapset, layer=None):
  345. """!Convenient method to build the unique identifier
  346. Existing layer and mapset definitions in the name string will be reused
  347. @param return the id of the vector map as name(:layer)@mapset while layer is optional
  348. """
  349. # Check if the name includes any mapset
  350. if name.find("@") >= 0:
  351. print name
  352. name, mapset = name.split("@")
  353. # Check for layer number in map name
  354. if name.find(":") >= 0:
  355. name, layer = name.split(":")
  356. if layer:
  357. return "%s:%s@%s"%(name, layer, mapset)
  358. else:
  359. return "%s@%s"%(name, mapset)
  360. def get_layer(self):
  361. """!Return the layer of the map or None in case no layer is defined"""
  362. return self.base.get_layer()
  363. def print_info(self):
  364. """!Print information about this class in human readable style"""
  365. if self.get_type() == "raster":
  366. # 1 2 3 4 5 6 7
  367. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  368. print ""
  369. print " +-------------------- Raster Dataset ----------------------------------------+"
  370. if self.get_type() == "raster3d":
  371. # 1 2 3 4 5 6 7
  372. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  373. print ""
  374. print " +-------------------- Raster3d Dataset --------------------------------------+"
  375. if self.get_type() == "vector":
  376. # 1 2 3 4 5 6 7
  377. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  378. print ""
  379. print " +-------------------- Vector Dataset ----------------------------------------+"
  380. print " | |"
  381. self.base.print_info()
  382. if self.is_time_absolute():
  383. self.absolute_time.print_info()
  384. if self.is_time_relative():
  385. self.relative_time.print_info()
  386. self.spatial_extent.print_info()
  387. self.metadata.print_info()
  388. datasets = self.get_registered_datasets()
  389. count = 0
  390. string = ""
  391. if datasets:
  392. for ds in datasets:
  393. if count > 0 and count % 3 == 0:
  394. string += "\n | ............................ "
  395. count = 0
  396. if count == 0:
  397. string += ds["id"]
  398. else:
  399. string += ",%s" % ds["id"]
  400. count += 1
  401. print " | Registered datasets ........ " + string
  402. if self.is_temporal_topology_build():
  403. self.print_temporal_topology_info()
  404. print " +----------------------------------------------------------------------------+"
  405. def print_shell_info(self):
  406. """!Print information about this class in shell style"""
  407. self.base.print_shell_info()
  408. if self.is_time_absolute():
  409. self.absolute_time.print_shell_info()
  410. if self.is_time_relative():
  411. self.relative_time.print_shell_info()
  412. self.spatial_extent.print_shell_info()
  413. self.metadata.print_shell_info()
  414. datasets = self.get_registered_datasets()
  415. count = 0
  416. string = ""
  417. if datasets:
  418. for ds in datasets:
  419. if count == 0:
  420. string += ds["id"]
  421. else:
  422. string += ",%s" % ds["id"]
  423. count += 1
  424. print "registered_datasets=" + string
  425. if self.is_temporal_topology_build():
  426. self.print_temporal_topology_shell_info()
  427. def insert(self, dbif=None, execute=True):
  428. """!Insert temporal dataset entry into database from the internal structure
  429. This functions assures that the timetsamp is written to the grass file system based database
  430. @param dbif: The database interface to be used
  431. @param execute: If True the SQL statements will be executed.
  432. If False the prepared SQL statements are returned and must be executed by the caller.
  433. """
  434. self.write_timestamp_to_grass()
  435. return abstract_dataset.insert(self, dbif, execute)
  436. def update(self, dbif=None, execute=True):
  437. """!Update temporal dataset entry of database from the internal structure
  438. excluding None variables
  439. This functions assures that the timetsamp is written to the grass file system based database
  440. @param dbif: The database interface to be used
  441. @param execute: If True the SQL statements will be executed.
  442. If False the prepared SQL statements are returned and must be executed by the caller.
  443. """
  444. self.write_timestamp_to_grass()
  445. return abstract_dataset.update(self, dbif, execute)
  446. def update_all(self, dbif=None, execute=True):
  447. """!Update temporal dataset entry of database from the internal structure
  448. and include None varuables.
  449. This functions assures that the timetsamp is written to the grass file system based database
  450. @param dbif: The database interface to be used
  451. @param execute: If True the SQL statements will be executed.
  452. If False the prepared SQL statements are returned and must be executed by the caller.
  453. """
  454. self.write_timestamp_to_grass()
  455. return abstract_dataset.update_all(self, dbif, execute)
  456. def set_absolute_time(self, start_time, end_time=None, timezone=None):
  457. """!Set the absolute time interval with start time and end time
  458. @param start_time: a datetime object specifying the start time of the map
  459. @param end_time: a datetime object specifying the end time of the map
  460. @param timezone: Thee timezone of the map
  461. """
  462. if start_time and not isinstance(start_time, datetime) :
  463. if self.get_layer():
  464. core.fatal(_("Start time must be of type datetime for %s map <%s> with layer: %s") % (self.get_type(), self.get_map_id(), self.get_layer()))
  465. else:
  466. core.fatal(_("Start time must be of type datetime for %s map <%s>") % (self.get_type(), self.get_map_id()))
  467. if end_time and not isinstance(end_time, datetime) :
  468. if self.get_layer():
  469. core.fatal(_("End time must be of type datetime for %s map <%s> with layer: %s") % (self.get_type(), self.get_map_id(), self.get_layer()))
  470. else:
  471. core.fatal(_("End time must be of type datetime for %s map <%s>") % (self.get_type(), self.get_map_id()))
  472. if start_time and end_time:
  473. if start_time > end_time:
  474. if self.get_layer():
  475. core.fatal(_("End time must be greater than start time for %s map <%s> with layer: %s") % (self.get_type(), self.get_map_id(), self.get_layer()))
  476. else:
  477. core.fatal(_("End time must be greater than start time for %s map <%s>") % (self.get_type(), self.get_map_id()))
  478. else:
  479. # Do not create an interval in case start and end time are equal
  480. if start_time == end_time:
  481. end_time = None
  482. self.base.set_ttype("absolute")
  483. self.absolute_time.set_start_time(start_time)
  484. self.absolute_time.set_end_time(end_time)
  485. self.absolute_time.set_timezone(timezone)
  486. def update_absolute_time(self, start_time, end_time=None, timezone=None, dbif = None):
  487. """!Update the absolute time
  488. This functions assures that the timetsamp is written to the grass file system based database
  489. @param start_time: a datetime object specifying the start time of the map
  490. @param end_time: a datetime object specifying the end time of the map
  491. @param timezone: Thee timezone of the map
  492. """
  493. dbif, connect = init_dbif(dbif)
  494. self.set_absolute_time(start_time, end_time, timezone)
  495. self.absolute_time.update_all(dbif)
  496. self.base.update(dbif)
  497. if connect == True:
  498. dbif.close()
  499. self.write_timestamp_to_grass()
  500. def set_relative_time(self, start_time, end_time, unit):
  501. """!Set the relative time interval
  502. @param start_time: A double value
  503. @param end_time: A double value
  504. @param unit: The unit of the relative time. Supported units: years, months, days, hours, minutes, seconds
  505. Return True for success and False otherwise
  506. """
  507. if not self.check_relative_time_unit(unit):
  508. if self.get_layer():
  509. core.error(_("Unsupported relative time unit type for %s map <%s> with layer %s: %s") % (self.get_type(), self.get_id(), self.get_layer(), unit))
  510. else:
  511. core.error(_("Unsupported relative time unit type for %s map <%s>: %s") % (self.get_type(), self.get_id(), unit))
  512. return False
  513. if start_time != None and end_time != None:
  514. if int(start_time) > int(end_time):
  515. if self.get_layer():
  516. core.error(_("End time must be greater than start time for %s map <%s> with layer %s") % (self.get_type(), self.get_id(), self.get_layer()))
  517. else:
  518. core.error(_("End time must be greater than start time for %s map <%s>") % (self.get_type(), self.get_id()))
  519. return False
  520. else:
  521. # Do not create an interval in case start and end time are equal
  522. if start_time == end_time:
  523. end_time = None
  524. self.base.set_ttype("relative")
  525. self.relative_time.set_unit(unit)
  526. self.relative_time.set_start_time(int(start_time))
  527. if end_time != None:
  528. self.relative_time.set_end_time(int(end_time))
  529. else:
  530. self.relative_time.set_end_time(None)
  531. return True
  532. def update_relative_time(self, start_time, end_time, unit, dbif = None):
  533. """!Update the relative time interval
  534. This functions assures that the timetsamp is written to the grass file system based database
  535. @param start_time: A double value
  536. @param end_time: A double value
  537. @param dbif: The database interface to be used
  538. """
  539. dbif, connect = init_dbif(dbif)
  540. if self.set_relative_time(start_time, end_time, unit):
  541. self.relative_time.update_all(dbif)
  542. self.base.update(dbif)
  543. if connect == True:
  544. dbif.close()
  545. self.write_timestamp_to_grass()
  546. def set_spatial_extent(self, north, south, east, west, top=0, bottom=0):
  547. """!Set the spatial extent of the map
  548. @param north: The northern edge
  549. @param south: The southern edge
  550. @param east: The eastern edge
  551. @param west: The western edge
  552. @param top: The top edge
  553. @param bottom: The bottom edge
  554. """
  555. self.spatial_extent.set_spatial_extent(north, south, east, west, top, bottom)
  556. def check_valid_time(self):
  557. """!Check for correct valid time"""
  558. if self.is_time_absolute():
  559. start, end, tz = self.get_absolute_time()
  560. else:
  561. start, end, unit = self.get_relative_time()
  562. if start != None:
  563. if end != None:
  564. if start >= end:
  565. if self.get_layer():
  566. core.error(_("Map <%s> with layer %s has incorrect time interval, start time is greater than end time") % (self.get_map_id(), self.get_layer()))
  567. else:
  568. core.error(_("Map <%s> has incorrect time interval, start time is greater than end time") % (self.get_map_id()))
  569. return False
  570. else:
  571. core.error(_("Map <%s> has incorrect start time") % (self.get_map_id()))
  572. return False
  573. return True
  574. def delete(self, dbif=None, update=True, execute = True):
  575. """!Delete a map entry from database if it exists
  576. Remove dependent entries:
  577. * Remove the map entry in each space time dataset in which this map is registered
  578. * Remove the space time dataset register table
  579. @param dbif: The database interface to be used
  580. @param update: Call for each unregister statement the update from registered maps
  581. of the space time dataset. This can slow down the un-registration process significantly.
  582. @param execute: If True the SQL DELETE and DROP table statements will be executed.
  583. If False the prepared SQL statements are returned and must be executed by the caller.
  584. @return The SQL statements if execute == False, else an empty string, None in case of a failure
  585. """
  586. dbif, connect = init_dbif(dbif)
  587. statement = ""
  588. if self.is_in_db(dbif):
  589. # SELECT all needed information from the database
  590. self.metadata.select(dbif)
  591. # First we unregister from all dependent space time datasets
  592. statement += self.unregister(dbif=dbif, update=update, execute=False)
  593. # Remove the strds register table
  594. if self.get_stds_register():
  595. statement += "DROP TABLE " + self.get_stds_register() + ";\n"
  596. core.verbose(_("Delete %s dataset <%s> from temporal database") % (self.get_type(), self.get_id()))
  597. # Delete yourself from the database, trigger functions will take care of dependencies
  598. statement += self.base.get_delete_statement()
  599. if execute == True:
  600. dbif.execute_transaction(statement)
  601. # Remove the timestamp from the file system
  602. self.remove_timestamp_from_grass()
  603. self.reset(None)
  604. if connect == True:
  605. dbif.close()
  606. if execute:
  607. return ""
  608. return statement
  609. def unregister(self, dbif=None, update=True, execute=True):
  610. """! Remove the map entry in each space time dataset in which this map is registered
  611. @param dbif: The database interface to be used
  612. @param update: Call for each unregister statement the update from registered maps
  613. of the space time dataset. This can slow down the un-registration process significantly.
  614. @param execute: If True the SQL DELETE and DROP table statements will be executed.
  615. If False the prepared SQL statements are returned and must be executed by the caller.
  616. @return The SQL statements if execute == False, else an empty string
  617. """
  618. if self.get_layer():
  619. core.verbose(_("Unregister %s map <%s> with layer %s from space time datasets") % \
  620. (self.get_type(), self.get_map_id(), self.get_layer()))
  621. else:
  622. core.verbose(_("Unregister %s map <%s> from space time datasets") % (self.get_type(), self.get_map_id()))
  623. statement = ""
  624. dbif, connect = init_dbif(dbif)
  625. # Get all datasets in which this map is registered
  626. rows = self.get_registered_datasets(dbif)
  627. # For each stds in which the map is registered
  628. if rows:
  629. count = 0
  630. num_sps = len(rows)
  631. for row in rows:
  632. core.percent(count, num_sps, 1)
  633. count += 1
  634. # Create a space time dataset object to remove the map
  635. # from its register
  636. stds = self.get_new_stds_instance(row["id"])
  637. stds.metadata.select(dbif)
  638. statement += stds.unregister_map(self, dbif, False)
  639. # Take care to update the space time dataset after
  640. # the map has been unregistered
  641. if update == True and execute == True:
  642. stds.update_from_registered_maps(dbif)
  643. core.percent(1, 1, 1)
  644. if execute == True:
  645. dbif.execute_transaction(statement)
  646. if connect == True:
  647. dbif.close()
  648. if execute:
  649. return ""
  650. return statement
  651. def get_registered_datasets(self, dbif=None):
  652. """!Return all space time dataset ids in which this map is registered as
  653. dictionary like rows with column "id" or None if this map is not registered in any
  654. space time dataset.
  655. @param dbif: The database interface to be used
  656. """
  657. dbif, connect = init_dbif(dbif)
  658. rows = None
  659. try:
  660. if self.get_stds_register() != None:
  661. # Select all stds tables in which this map is registered
  662. sql = "SELECT id FROM " + self.get_stds_register()
  663. dbif.cursor.execute(sql)
  664. rows = dbif.cursor.fetchall()
  665. except:
  666. core.error(_("Unable to select space time dataset register table <%s>") % (self.get_stds_register()))
  667. if connect == True:
  668. dbif.close()
  669. return rows