abstract_space_time_dataset.py 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  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 temporal_granularity import *
  18. ###############################################################################
  19. class abstract_space_time_dataset(abstract_dataset):
  20. """Abstract space time dataset class
  21. This class represents a space time dataset. Convenient functions
  22. to select, update, insert or delete objects of this type in the SQL
  23. temporal database exists as well as functions to register or unregister
  24. raster maps.
  25. Parts of the temporal logic are implemented in the SQL temporal database,
  26. like the computation of the temporal and spatial extent as well as the
  27. collecting of metadata.
  28. """
  29. def __init__(self, ident):
  30. self.reset(ident)
  31. self.map_counter = 0
  32. def get_new_map_instance(self, ident=None):
  33. """Return a new instance of a map dataset which is associated with the type of this class
  34. @param ident: The unique identifier of the new object
  35. """
  36. raise IOError("This method must be implemented in the subclasses")
  37. def get_map_register(self):
  38. """Return the name of the map register table"""
  39. raise IOError("This method must be implemented in the subclasses")
  40. def set_map_register(self, name):
  41. """Set the name of the map register table
  42. This table stores all map names which are registered in this space time dataset.
  43. @param name: The name of the register table
  44. """
  45. raise IOError("This method must be implemented in the subclasses")
  46. def print_self(self):
  47. """Print the content of the internal structure to stdout"""
  48. self.base.print_self()
  49. if self.is_time_absolute():
  50. self.absolute_time.print_self()
  51. if self.is_time_relative():
  52. self.relative_time.print_self()
  53. self.spatial_extent.print_self()
  54. self.metadata.print_self()
  55. def print_info(self):
  56. """Print information about this class in human readable style"""
  57. if self.get_type() == "strds":
  58. # 1 2 3 4 5 6 7
  59. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  60. print ""
  61. print " +-------------------- Space Time Raster Dataset -----------------------------+"
  62. if self.get_type() == "str3ds":
  63. # 1 2 3 4 5 6 7
  64. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  65. print ""
  66. print " +-------------------- Space Time Raster3d Dataset ---------------------------+"
  67. if self.get_type() == "stvds":
  68. # 1 2 3 4 5 6 7
  69. # 0123456789012345678901234567890123456789012345678901234567890123456789012345678
  70. print ""
  71. print " +-------------------- Space Time Vector Dataset -----------------------------+"
  72. print " | |"
  73. self.base.print_info()
  74. if self.is_time_absolute():
  75. self.absolute_time.print_info()
  76. if self.is_time_relative():
  77. self.relative_time.print_info()
  78. self.spatial_extent.print_info()
  79. self.metadata.print_info()
  80. print " +----------------------------------------------------------------------------+"
  81. def print_shell_info(self):
  82. """Print information about this class in shell style"""
  83. self.base.print_shell_info()
  84. if self.is_time_absolute():
  85. self.absolute_time.print_shell_info()
  86. if self.is_time_relative():
  87. self.relative_time.print_shell_info()
  88. self.spatial_extent.print_shell_info()
  89. self.metadata.print_shell_info()
  90. def set_initial_values(self, temporal_type, semantic_type, \
  91. title=None, description=None):
  92. """Set the initial values of the space time dataset
  93. @param temporal_type: The temporal type of this space time dataset (absolute or relative)
  94. @param semantic_type: The semantic type of this dataset
  95. @param title: The title
  96. @param description: The description of this dataset
  97. """
  98. if temporal_type == "absolute":
  99. self.set_time_to_absolute()
  100. elif temporal_type == "relative":
  101. self.set_time_to_relative()
  102. else:
  103. core.fatal(_("Unknown temporal type \"%s\"") % (temporal_type))
  104. self.base.set_semantic_type(semantic_type)
  105. self.metadata.set_title(title)
  106. self.metadata.set_description(description)
  107. def get_semantic_type(self):
  108. """Return the semantic type of this dataset"""
  109. return self.base.get_semantic_type()
  110. def get_initial_values(self):
  111. """Return the initial values: temporal_type, semantic_type, title, description"""
  112. temporal_type = self.get_temporal_type()
  113. semantic_type = self.base.get_semantic_type()
  114. title = self.metadata.get_title()
  115. description = self.metadata.get_description()
  116. return temporal_type, semantic_type, title, description
  117. def get_granularity(self):
  118. """Return the granularity"""
  119. temporal_type = self.get_temporal_type()
  120. if temporal_type == "absolute":
  121. granularity = self.absolute_time.get_granularity()
  122. elif temporal_type == "relative":
  123. granularity = self.relative_time.get_granularity()
  124. return granularity
  125. def set_granularity(self, granularity):
  126. temporal_type = self.get_temporal_type()
  127. if temporal_type == "absolute":
  128. self.set_time_to_absolute()
  129. self.absolute_time.set_granularity(granularity)
  130. elif temporal_type == "relative":
  131. self.set_time_to_relative()
  132. self.relative_time.set_granularity(granularity)
  133. else:
  134. core.fatal(_("Unknown temporal type \"%s\"") % (temporal_type))
  135. def set_relative_time_unit(self, unit):
  136. """Set the relative time unit which may be of type: years, months, days, hours, minutes or seconds
  137. All maps registered in a (relative time) space time dataset must have the same unit
  138. """
  139. temporal_type = self.get_temporal_type()
  140. if temporal_type == "relative":
  141. if not self.check_relative_time_unit(unit):
  142. core.fatal(_("Unsupported temporal unit: %s") % (unit))
  143. self.relative_time.set_unit(unit)
  144. def get_map_time(self):
  145. """Return the type of the map time, interval, point, mixed or invalid"""
  146. temporal_type = self.get_temporal_type()
  147. if temporal_type == "absolute":
  148. map_time = self.absolute_time.get_map_time()
  149. elif temporal_type == "relative":
  150. map_time = self.relative_time.get_map_time()
  151. return map_time
  152. def print_temporal_relation_matrix(self, maps):
  153. """Print the temporal relation matrix of all registered maps to stdout
  154. The temporal relation matrix includes the temporal relations between
  155. all registered maps. The relations are strings stored in a list of lists.
  156. @param dbif: The database interface to be used
  157. """
  158. for i in range(len(maps)):
  159. reltations = ""
  160. count = 0
  161. for j in range(i + 1, len(maps)):
  162. relation = maps[j].temporal_relation(maps[i])
  163. # print maps[j].base.get_name(), maps[i].base.get_name(), relation
  164. if count == 0:
  165. relations = relation
  166. else:
  167. relations += "," + str(relation)
  168. count += 1
  169. # Break if the the next map follows
  170. if relation == "follows":
  171. break
  172. # Break if the the next map is after
  173. if relation == "after":
  174. break
  175. if i < len(maps) - 1:
  176. print maps[i].base.get_name(), relations
  177. else:
  178. print maps[i].base.get_name()
  179. def get_temporal_relation_matrix(self, maps):
  180. """Return the temporal relation matrix of all registered maps as list of lists
  181. The map list must be ordered by start time
  182. The temporal relation matrix includes the temporal relations between
  183. all registered maps. The relations are strings stored in a list of lists.
  184. @param maps: a ordered by start_time list of map objects
  185. """
  186. matrix = []
  187. # Create the temporal relation matrix
  188. # Add the map names first
  189. row = []
  190. for map in maps:
  191. row.append(map.get_id())
  192. matrix.append(row)
  193. for mapA in maps:
  194. row = []
  195. for mapB in maps:
  196. row.append(mapA.temporal_relation(mapB))
  197. matrix.append(row)
  198. return matrix
  199. def count_temporal_types(self, maps):
  200. """Return the temporal type of the registered maps as dictionary
  201. The map list must be ordered by start time
  202. The temporal type can be:
  203. * point -> only the start time is present
  204. * interval -> start and end time
  205. * invalid -> No valid time point or interval found
  206. @param maps: A sorted (start_time) list of abstract_dataset objects
  207. """
  208. time_invalid = 0
  209. time_point = 0
  210. time_interval = 0
  211. tcount = {}
  212. for i in range(len(maps)):
  213. # Check for point and interval data
  214. if maps[i].is_time_absolute():
  215. start, end, tz = maps[i].get_absolute_time()
  216. if maps[i].is_time_relative():
  217. start, end, unit = maps[i].get_relative_time()
  218. if start != None and end != None:
  219. time_interval += 1
  220. elif start != None and end == None:
  221. time_point += 1
  222. else:
  223. time_invalid += 1
  224. tcount["point"] = time_point
  225. tcount["interval"] = time_interval
  226. tcount["invalid"] = time_invalid
  227. return tcount
  228. def count_gaps(self, maps):
  229. """Count the number of gaps between temporal neighbors
  230. @param maps: A sorted (start_time) list of abstract_dataset objects
  231. @return The numbers of gaps between temporal neighbors
  232. """
  233. gaps = 0
  234. # Check for gaps
  235. for i in range(len(maps)):
  236. if i < len(maps) - 1:
  237. relation = maps[i + 1].temporal_relation(maps[i])
  238. if relation == "after":
  239. gaps += 1
  240. return gaps
  241. def count_temporal_relations(self, maps):
  242. """Count the temporal relations between the registered maps.
  243. The map list must be ordered by start time
  244. @param maps: A sorted (start_time) list of abstract_dataset objects
  245. @return A dictionary with counted temporal relationships
  246. """
  247. tcount = {}
  248. for i in range(len(maps)):
  249. # Check for point and interval data
  250. for j in range(i + 1, len(maps)):
  251. relation = maps[j].temporal_relation(maps[i])
  252. if tcount.has_key(relation):
  253. tcount[relation] = tcount[relation] + 1
  254. else:
  255. tcount[relation] = 1
  256. # Break if the the next map follows
  257. if relation == "follows":
  258. break
  259. # Break if the the next map is after
  260. if relation == "after":
  261. break
  262. return tcount
  263. def check_temporal_topology(self, maps=None, dbif=None):
  264. """Check the temporal topology
  265. Correct topology means, that time intervals are not overlap or
  266. that intervals does not contain other intervals. Equal time intervals or
  267. points of time are not allowed.
  268. The map list must be ordered by start time
  269. Allowed and not allowed temporal relationships for correct topology
  270. after -> allowed
  271. before -> allowed
  272. follows -> allowed
  273. precedes -> allowed
  274. equivalent -> not allowed
  275. during -> not allowed
  276. contains -> not allowed
  277. overlaps -> not allowed
  278. overlapped -> not allowed
  279. starts -> not allowed
  280. finishes -> not allowed
  281. started -> not allowed
  282. finished -> not allowed
  283. @param maps: A sorted (start_time) list of abstract_dataset objects
  284. @return True if topology is correct
  285. """
  286. if maps == None:
  287. maps = get_registered_maps_as_objects(where=None, order="start_time", dbif=dbif)
  288. relations = self.count_temporal_relations(maps)
  289. map_time = self.get_map_time()
  290. if map_time == "interval" or map_time == "mixed":
  291. if relations.has_key("equivalent"):
  292. return False
  293. if relations.has_key("during"):
  294. return False
  295. if relations.has_key("contains"):
  296. return False
  297. if relations.has_key("overlaps"):
  298. return False
  299. if relations.has_key("overlapped"):
  300. return False
  301. if relations.has_key("starts"):
  302. return False
  303. if relations.has_key("finishes"):
  304. return False
  305. if relations.has_key("started"):
  306. return False
  307. if relations.has_key("finished"):
  308. return False
  309. elif map_time == "point":
  310. if relations.has_key("equivalent"):
  311. return False
  312. else:
  313. return False
  314. return True
  315. def sample_by_dataset_topology(self, stds, method=None, dbif=None):
  316. """Sample this space time dataset with the temporal topology of a second space time dataset
  317. The sample dataset must have "interval" as temporal map type, so all sample maps have valid interval time.
  318. Return all registered maps as ordered (by start_time) object list with
  319. "gap" map objects (id==None). Each list entry is a list of map objects
  320. which are potentially located in temporal relation to the actual granule of the second space time dataset.
  321. Each entry in the object list is a dict. The actual sampler map and its temporal extent (the actual granule) and
  322. the list of samples are stored:
  323. list = self.sample_by_dataset_topology(stds=sampler, method=["during","overlap","contain","equal"])
  324. for entry in list:
  325. granule = entry["granule"]
  326. maplist = entry["samples"]
  327. for map in maplist:
  328. map.select()
  329. map.print_info()
  330. A valid temporal topology (no overlapping or inclusion allowed) is needed to get correct results in case of gaps
  331. in the sample dataset.
  332. Gaps between maps are identified as unregistered maps with id==None.
  333. The map objects are initialized with the id and the temporal extent of the granule (temporal type, start time, end time).
  334. In case more map informations are needed, use the select() method for each listed object.
  335. @param stds: The space time dataset to be used for temporal sampling
  336. @param method: This option specifies what sample method should be used. In case the registered maps are of temporal point type,
  337. only the start time is used for sampling. In case of mixed of interval data the user can chose between:
  338. * start: Select maps of which the start time is located in the selection granule
  339. map : s
  340. granule: s-----------------e
  341. map : s--------------------e
  342. granule: s-----------------e
  343. map : s--------e
  344. granule: s-----------------e
  345. * during: Select maps which are temporal during the selection granule
  346. map : s-----------e
  347. granule: s-----------------e
  348. * overlap: Select maps which temporal overlap the selection granule
  349. map : s-----------e
  350. granule: s-----------------e
  351. map : s-----------e
  352. granule: s----------e
  353. * contain: Select maps which temporally contain the selection granule
  354. map : s-----------------e
  355. granule: s-----------e
  356. * equal: Select maps which temporally equal to the selection granule
  357. map : s-----------e
  358. granule: s-----------e
  359. All these methods can be combined. Method must be of type tuple including the identification strings.
  360. @param dbif: The database interface to be used
  361. In case nothing found None is returned
  362. """
  363. use_start = False
  364. use_during = False
  365. use_overlap = False
  366. use_contain = False
  367. use_equal = False
  368. # Initialize the methods
  369. if method:
  370. for name in method:
  371. if name == "start":
  372. use_start = True
  373. if name == "during":
  374. use_during = True
  375. if name == "overlap":
  376. use_overlap = True
  377. if name == "contain":
  378. use_contain = True
  379. if name == "equal":
  380. use_equal = True
  381. else:
  382. use_during = True
  383. use_overlap = True
  384. use_contain = True
  385. use_equal = True
  386. if self.get_temporal_type() != stds.get_temporal_type():
  387. core.error(_("The space time datasets must be of the same temporal type"))
  388. return None
  389. if stds.get_map_time() != "interval":
  390. core.error(_("The temporal map type of the sample dataset must be interval"))
  391. return None
  392. # In case points of time are available, disable the interval specific methods
  393. if self.get_map_time() == "point":
  394. use_start = True
  395. use_during = False
  396. use_overlap = False
  397. use_contain = False
  398. use_equal = False
  399. connect = False
  400. if dbif == None:
  401. dbif = sql_database_interface()
  402. dbif.connect()
  403. connect = True
  404. obj_list = []
  405. sample_maps = stds.get_registered_maps_as_objects_with_gaps(where=None, dbif=dbif)
  406. for granule in sample_maps:
  407. start, end = granule.get_valid_time()
  408. where = create_temporal_relation_sql_where_statement(start, end, use_start, \
  409. use_during, use_overlap, use_contain, use_equal)
  410. rows = self.get_registered_maps("id", where, "start_time", dbif)
  411. result = {}
  412. result["granule"] = granule
  413. maplist = None
  414. if rows:
  415. maplist = []
  416. for row in rows:
  417. map = self.get_new_map_instance(row["id"])
  418. if self.is_time_absolute():
  419. map.set_absolute_time(start, end)
  420. elif self.is_time_relative():
  421. map.set_relative_time(start, end, self.get_relative_time_unit())
  422. maplist.append(copy.copy(map))
  423. result["samples"] = maplist
  424. else:
  425. maplist = []
  426. map = self.get_new_map_instance(None)
  427. if self.is_time_absolute():
  428. map.set_absolute_time(start, end)
  429. elif self.is_time_relative():
  430. map.set_relative_time(start, end, self.get_relative_time_unit())
  431. maplist.append(copy.copy(map))
  432. result["samples"] = maplist
  433. obj_list.append(copy.copy(result))
  434. if connect == True:
  435. dbif.close()
  436. return obj_list
  437. def get_registered_maps_as_objects_by_granularity(self, gran=None, dbif=None):
  438. """Return all registered maps as ordered (by start_time) object list with
  439. "gap" map objects (id==None) for temporal topological operations using the
  440. granularity of the space time dataset as increment. Each list entry is a list of map objects
  441. which are potentially located in the actual granule.
  442. A valid temporal topology (no overlapping or inclusion allowed) is needed to get correct results.
  443. The dataset must have "interval" as temporal map type, so all maps have valid interval time.
  444. Gaps between maps are identified as unregistered maps with id==None.
  445. The objects are initialized with the id and the temporal extent (temporal type, start time, end time).
  446. In case more map informations are needed, use the select() method for each listed object.
  447. @param gran: The granularity to be used
  448. @param dbif: The database interface to be used
  449. In case nothing found None is returned
  450. """
  451. connect = False
  452. if dbif == None:
  453. dbif = sql_database_interface()
  454. dbif.connect()
  455. connect = True
  456. obj_list = []
  457. if gran == None:
  458. gran = self.get_granularity()
  459. start, end = self.get_valid_time()
  460. while start < end:
  461. if self.is_time_absolute():
  462. next = increment_datetime_by_string(start, gran)
  463. else:
  464. next = start + gran
  465. where = "(start_time <= '%s' and end_time >= '%s')" % (start, next)
  466. rows = self.get_registered_maps("id", where, "start_time", dbif)
  467. if rows:
  468. if len(rows) > 1:
  469. core.warning(_("More than one map found in a granule. Temporal granularity seems to be invalid or the chosen granularity is not a greatest common divider of all intervals and gaps in the dataset."))
  470. maplist = []
  471. for row in rows:
  472. # Take the first map
  473. map = self.get_new_map_instance(rows[0]["id"])
  474. if self.is_time_absolute():
  475. map.set_absolute_time(start, next)
  476. elif self.is_time_relative():
  477. map.set_relative_time(start, next, self.get_relative_time_unit())
  478. maplist.append(copy.copy(map))
  479. obj_list.append(copy.copy(maplist))
  480. start = next
  481. if connect == True:
  482. dbif.close()
  483. return obj_list
  484. def get_registered_maps_as_objects_with_gaps(self, where=None, dbif=None):
  485. """Return all registered maps as ordered (by start_time) object list with
  486. "gap" map objects (id==None) for temporal topological operations
  487. Gaps between maps are identified as maps with id==None
  488. The objects are initialized with the id and the temporal extent (temporal type, start time, end time).
  489. In case more map informations are needed, use the select() method for each listed object.
  490. @param where: The SQL where statement to select a subset of the registered maps without "WHERE"
  491. @param dbif: The database interface to be used
  492. In case nothing found None is returned
  493. """
  494. connect = False
  495. if dbif == None:
  496. dbif = sql_database_interface()
  497. dbif.connect()
  498. connect = True
  499. obj_list = []
  500. maps = self.get_registered_maps_as_objects(where, "start_time", dbif)
  501. if maps and len(maps) > 0:
  502. for i in range(len(maps)):
  503. obj_list.append(maps[i])
  504. # Detect and insert gaps
  505. if i < len(maps) - 1:
  506. relation = maps[i + 1].temporal_relation(maps[i])
  507. if relation == "after":
  508. start1, end1 = maps[i].get_valid_time()
  509. start2, end2 = maps[i + 1].get_valid_time()
  510. end = start2
  511. if end1:
  512. start = end1
  513. else:
  514. start = start1
  515. map = self.get_new_map_instance(None)
  516. if self.is_time_absolute():
  517. map.set_absolute_time(start, end)
  518. elif self.is_time_relative():
  519. map.set_relative_time(start, end, self.get_relative_time_unit())
  520. obj_list.append(copy.copy(map))
  521. if connect == True:
  522. dbif.close()
  523. return obj_list
  524. def get_registered_maps_as_objects(self, where=None, order="start_time", dbif=None):
  525. """Return all registered maps as ordered object list for temporal topological operations
  526. The objects are initialized with the id and the temporal extent (temporal type, start time, end time).
  527. In case more map informations are needed, use the select() method for each listed object.
  528. @param where: The SQL where statement to select a subset of the registered maps without "WHERE"
  529. @param order: The SQL order statement to be used to order the objects in the list without "ORDER BY"
  530. @param dbif: The database interface to be used
  531. In case nothing found None is returned
  532. """
  533. connect = False
  534. if dbif == None:
  535. dbif = sql_database_interface()
  536. dbif.connect()
  537. connect = True
  538. obj_list = []
  539. rows = self.get_registered_maps("id,start_time,end_time", where, order, dbif)
  540. count = 0
  541. if rows:
  542. for row in rows:
  543. core.percent(count, len(rows), 1)
  544. map = self.get_new_map_instance(row["id"])
  545. if self.is_time_absolute():
  546. map.set_absolute_time(row["start_time"], row["end_time"])
  547. elif self.is_time_relative():
  548. map.set_relative_time(row["start_time"], row["end_time"], self.get_relative_time_unit())
  549. obj_list.append(copy.copy(map))
  550. count += 1
  551. core.percent(1, 1, 1)
  552. if connect == True:
  553. dbif.close()
  554. return obj_list
  555. def get_registered_maps(self, columns=None, where = None, order = None, dbif=None):
  556. """Return sqlite rows of all registered maps.
  557. In case columns are not specified, each row includes all columns specified in the datatype specific view
  558. @param columns: Columns to be selected as SQL compliant string
  559. @param where: The SQL where statement to select a subset of the registered maps without "WHERE"
  560. @param order: The SQL order statement to be used to order the objects in the list without "ORDER BY"
  561. @param dbif: The database interface to be used
  562. In case nothing found None is returned
  563. """
  564. connect = False
  565. if dbif == None:
  566. dbif = sql_database_interface()
  567. dbif.connect()
  568. connect = True
  569. rows = None
  570. if self.get_map_register():
  571. # Use the correct temporal table
  572. if self.get_temporal_type() == "absolute":
  573. map_view = self.get_new_map_instance(None).get_type() + "_view_abs_time"
  574. else:
  575. map_view = self.get_new_map_instance(None).get_type() + "_view_rel_time"
  576. if columns:
  577. sql = "SELECT %s FROM %s WHERE %s.id IN (SELECT id FROM %s)" % (columns, map_view, map_view, self.get_map_register())
  578. else:
  579. sql = "SELECT * FROM %s WHERE %s.id IN (SELECT id FROM %s)" % (map_view, map_view, self.get_map_register())
  580. if where:
  581. sql += " AND %s" % (where)
  582. if order:
  583. sql += " ORDER BY %s" % (order)
  584. try:
  585. dbif.cursor.execute(sql)
  586. rows = dbif.cursor.fetchall()
  587. except:
  588. if connect == True:
  589. dbif.close()
  590. core.error(_("Unable to get map ids from register table <%s>") % (self.get_map_register()))
  591. raise
  592. if connect == True:
  593. dbif.close()
  594. return rows
  595. def delete(self, dbif=None):
  596. """Delete a space time dataset from the temporal database
  597. This method removes the space time dataset from the temporal database and drops its map register table
  598. @param dbif: The database interface to be used
  599. """
  600. # First we need to check if maps are registered in this dataset and
  601. # unregister them
  602. core.verbose(_("Delete space time %s dataset <%s> from temporal database") % (self.get_new_map_instance(ident=None).get_type(), self.get_id()))
  603. connect = False
  604. if dbif == None:
  605. dbif = sql_database_interface()
  606. dbif.connect()
  607. connect = True
  608. # SELECT all needed informations from the database
  609. self.select(dbif)
  610. core.verbose(_("Drop map register table: %s") % (self.get_map_register()))
  611. if self.get_map_register():
  612. rows = self.get_registered_maps("id", None, None, dbif)
  613. # Unregister each registered map in the table
  614. if rows:
  615. num_maps = len(rows)
  616. count = 0
  617. for row in rows:
  618. core.percent(count, num_maps, 1)
  619. # Unregister map
  620. map = self.get_new_map_instance(row["id"])
  621. self.unregister_map(map, dbif)
  622. count += 1
  623. core.percent(1, 1, 1)
  624. try:
  625. # Drop the map register table
  626. sql = "DROP TABLE " + self.get_map_register()
  627. dbif.cursor.execute(sql)
  628. dbif.connection.commit()
  629. except:
  630. if connect == True:
  631. dbif.close()
  632. core.error(_("Unable to drop table <%s>") % (self.get_map_register()))
  633. raise
  634. # Remove the primary key, the foreign keys will be removed by trigger
  635. self.base.delete(dbif)
  636. self.reset(None)
  637. if connect == True:
  638. dbif.close()
  639. def register_map(self, map, dbif=None):
  640. """ Register a map in the space time dataset.
  641. This method takes care of the registration of a map
  642. in a space time dataset.
  643. In case the map is already registered this function will break with a warning
  644. and return False
  645. @param dbif: The database interface to be used
  646. """
  647. connect = False
  648. if dbif == None:
  649. dbif = sql_database_interface()
  650. dbif.connect()
  651. connect = True
  652. if map.is_in_db(dbif) == False:
  653. dbif.close()
  654. core.fatal(_("Only maps with absolute or relative valid time can be registered"))
  655. if map.get_layer():
  656. core.verbose(_("Register %s map <%s> with layer %s in space time %s dataset <%s>") % (map.get_type(), map.get_map_id(), map.get_layer(), map.get_type(), self.get_id()))
  657. else:
  658. core.verbose(_("Register %s map <%s> in space time %s dataset <%s>") % (map.get_type(), map.get_map_id(), map.get_type(), self.get_id()))
  659. # First select all data from the database
  660. map.select(dbif)
  661. if not map.check_valid_time():
  662. if map.get_layer():
  663. core.fatal(_("Map <%s> with layer %s has invalid time") % map.get_map_id(), map.get_layer())
  664. else:
  665. core.fatal(_("Map <%s> has invalid time") % map.get_map_id())
  666. map_id = map.base.get_id()
  667. map_name = map.base.get_name()
  668. map_mapset = map.base.get_mapset()
  669. map_register_table = map.get_stds_register()
  670. map_rel_time_unit = map.get_relative_time_unit()
  671. map_ttype = map.get_temporal_type()
  672. #print "Map register table", map_register_table
  673. # Get basic info
  674. stds_name = self.base.get_name()
  675. stds_mapset = self.base.get_mapset()
  676. stds_register_table = self.get_map_register()
  677. stds_ttype = self.get_temporal_type()
  678. # Check temporal types
  679. if stds_ttype != map_ttype:
  680. if map.get_layer():
  681. core.fatal(_("Temporal type of space time dataset <%s> and map <%s> with layer %s are different") % (self.get_id(), map.get_map_id(), map.get_layer()))
  682. else:
  683. core.fatal(_("Temporal type of space time dataset <%s> and map <%s> are different") % (self.get_id(), map.get_map_id()))
  684. # In case no map has been registered yet, set the relative time unit from the first map
  685. if (self.metadata.get_number_of_maps() == None or self.metadata.get_number_of_maps() == 0) and \
  686. self.map_counter == 0 and self.is_time_relative():
  687. self.set_relative_time_unit(map_rel_time_unit)
  688. self.relative_time.update()
  689. core.verbose(_("Set temporal unit for space time %s dataset <%s> to %s") % (map.get_type(), self.get_id(), map_rel_time_unit))
  690. stds_rel_time_unit = self.get_relative_time_unit()
  691. # Check the relative time unit
  692. if self.is_time_relative() and (stds_rel_time_unit != map_rel_time_unit):
  693. if map.get_layer():
  694. core.fatal(_("Relative time units of space time dataset <%s> and map <%s> with layer %s are different") % (self.get_id(), map.get_map_id(), map.get_layer()))
  695. else:
  696. core.fatal(_("Relative time units of space time dataset <%s> and map <%s> are different") % (self.get_id(), map.get_map_id()))
  697. #print "STDS register table", stds_register_table
  698. if stds_mapset != map_mapset:
  699. dbif.close()
  700. core.fatal(_("Only maps from the same mapset can be registered"))
  701. # Check if map is already registered
  702. if stds_register_table:
  703. if dbmi.paramstyle == "qmark":
  704. sql = "SELECT id FROM " + stds_register_table + " WHERE id = (?)"
  705. else:
  706. sql = "SELECT id FROM " + stds_register_table + " WHERE id = (%s)"
  707. dbif.cursor.execute(sql, (map_id,))
  708. row = dbif.cursor.fetchone()
  709. # In case of no entry make a new one
  710. if row and row[0] == map_id:
  711. if connect == True:
  712. dbif.close()
  713. if map.get_layer():
  714. core.warning(_("Map <%s> with layer %s is already registered.") % (map.get_map_id(), map.get_layer()))
  715. else:
  716. core.warning(_("Map <%s> is already registered.") % (map.get_map_id()))
  717. return False
  718. # Create tables
  719. sql_path = get_sql_template_path()
  720. # We need to create the map raster register table before we can register the map
  721. if map_register_table == None:
  722. # Create a unique id
  723. uuid_rand = "map_" + str(uuid.uuid4()).replace("-", "")
  724. map_register_table = uuid_rand + "_" + self.get_type() + "_register"
  725. # Read the SQL template
  726. sql = open(os.path.join(sql_path, "map_stds_register_table_template.sql"), 'r').read()
  727. # Create the raster, raster3d and vector tables
  728. sql = sql.replace("GRASS_MAP", map.get_type())
  729. sql = sql.replace("MAP_NAME", map_name + "_" + map_mapset )
  730. sql = sql.replace("TABLE_NAME", uuid_rand )
  731. sql = sql.replace("MAP_ID", map_id)
  732. sql = sql.replace("STDS", self.get_type())
  733. try:
  734. if dbmi.__name__ == "sqlite3":
  735. dbif.cursor.executescript(sql)
  736. else:
  737. dbif.cursor.execute(sql)
  738. except:
  739. if connect == True:
  740. dbif.close()
  741. if map.get_layer():
  742. core.error(_("Unable to create the space time %s dataset register table for map <%s> with layer %s") % \
  743. (map.get_type(), map.get_map_id(), map.get_layer()))
  744. else:
  745. core.error(_("Unable to create the space time %s dataset register table for <%s>") % \
  746. (map.get_type(), map.get_map_id()))
  747. raise
  748. # Set the stds register table name and put it into the DB
  749. map.set_stds_register(map_register_table)
  750. map.metadata.update(dbif)
  751. if map.get_layer():
  752. core.verbose(_("Created register table <%s> for %s map <%s> with layer %s") % \
  753. (map_register_table, map.get_type(), map.get_map_id(), map.get_layer()))
  754. else:
  755. core.verbose(_("Created register table <%s> for %s map <%s>") % \
  756. (map_register_table, map.get_type(), map.get_map_id()))
  757. # We need to create the table and register it
  758. if stds_register_table == None:
  759. # Create table name
  760. stds_register_table = stds_name + "_" + stds_mapset + "_" + map.get_type() + "_register"
  761. # Read the SQL template
  762. sql = open(os.path.join(sql_path, "stds_map_register_table_template.sql"), 'r').read()
  763. # Create the raster, raster3d and vector tables
  764. sql = sql.replace("GRASS_MAP", map.get_type())
  765. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  766. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  767. sql = sql.replace("STDS", self.get_type())
  768. sql_script = ""
  769. sql_script += "BEGIN TRANSACTION;\n"
  770. sql_script += sql
  771. sql_script += "\n"
  772. sql_script += "END TRANSACTION;"
  773. try:
  774. if dbmi.__name__ == "sqlite3":
  775. dbif.cursor.executescript(sql_script)
  776. else:
  777. dbif.cursor.execute(sql_script)
  778. dbif.connection.commit()
  779. except:
  780. if connect == True:
  781. dbif.close()
  782. if map.get_layer():
  783. core.error(_("Unable to create the space time %s dataset register table for map <%s> with layer %s") % \
  784. (map.get_type(), map.get_map_id(), map.get_layer()))
  785. else:
  786. core.error(_("Unable to create the space time %s dataset register table for <%s>") % \
  787. (map.get_type(), map.get_map_id()))
  788. raise
  789. # Set the map register table name and put it into the DB
  790. self.set_map_register(stds_register_table)
  791. self.metadata.update(dbif)
  792. core.verbose(_("Created register table <%s> for space time %s dataset <%s>") % \
  793. (stds_register_table, map.get_type(), self.get_id()))
  794. # Register the stds in the map stds register table
  795. # Check if the entry is already there
  796. if dbmi.paramstyle == "qmark":
  797. sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
  798. else:
  799. sql = "SELECT id FROM " + map_register_table + " WHERE id = %s"
  800. dbif.cursor.execute(sql, (self.base.get_id(),))
  801. row = dbif.cursor.fetchone()
  802. # In case of no entry make a new one
  803. if row == None:
  804. if dbmi.paramstyle == "qmark":
  805. sql = "INSERT INTO " + map_register_table + " (id) " + "VALUES (?)"
  806. else:
  807. sql = "INSERT INTO " + map_register_table + " (id) " + "VALUES (%s)"
  808. #print sql
  809. dbif.cursor.execute(sql, (self.base.get_id(),))
  810. # Now put the raster name in the stds map register table
  811. if dbmi.paramstyle == "qmark":
  812. sql = "INSERT INTO " + stds_register_table + " (id) " + "VALUES (?)"
  813. else:
  814. sql = "INSERT INTO " + stds_register_table + " (id) " + "VALUES (%s)"
  815. #print sql
  816. dbif.cursor.execute(sql, (map_id,))
  817. if connect == True:
  818. dbif.close()
  819. # increase the counter
  820. self.map_counter += 1
  821. return True
  822. def unregister_map(self, map, dbif = None):
  823. """Unregister a map from the space time dataset.
  824. This method takes care of the un-registration of a map
  825. from a space time dataset.
  826. @param map: The map object to unregister
  827. @param dbif: The database interface to be used
  828. """
  829. connect = False
  830. if dbif == None:
  831. dbif = sql_database_interface()
  832. dbif.connect()
  833. connect = True
  834. if map.is_in_db(dbif) == False:
  835. dbif.close()
  836. if map.get_layer():
  837. core.fatal(_("Unable to find map <%s> with layer %s in temporal database") % (map.get_map_id(), map.get_layer()))
  838. else:
  839. core.fatal(_("Unable to find map <%s> in temporal database") % (map.get_map_id()))
  840. if map.get_layer():
  841. core.verbose(_("Unregister %s map <%s> with layer %s") % (map.get_type(), map.get_map_id(), map.get_layer()))
  842. else:
  843. core.verbose(_("Unregister %s map <%s>") % (map.get_type(), map.get_map_id()))
  844. # First select all data from the database
  845. map.select(dbif)
  846. map_id = map.get_id()
  847. map_register_table = map.get_stds_register()
  848. stds_register_table = self.get_map_register()
  849. # Check if the map is registered in the space time raster dataset
  850. if dbmi.paramstyle == "qmark":
  851. sql = "SELECT id FROM " + map_register_table + " WHERE id = ?"
  852. else:
  853. sql = "SELECT id FROM " + map_register_table + " WHERE id = %s"
  854. dbif.cursor.execute(sql, (self.base.get_id(),))
  855. row = dbif.cursor.fetchone()
  856. # Break if the map is not registered
  857. if row == None:
  858. if map.get_layer():
  859. core.warning(_("Map <%s> with layer %s is not registered in space time dataset <%s>") %(map.get_map_id(), map.get_layer(), self.base.get_id()))
  860. else:
  861. core.warning(_("Map <%s> is not registered in space time dataset <%s>") %(map.get_map_id(), self.base.get_id()))
  862. if connect == True:
  863. dbif.close()
  864. return False
  865. # Remove the space time raster dataset from the raster dataset register
  866. if map_register_table != None:
  867. if dbmi.paramstyle == "qmark":
  868. sql = "DELETE FROM " + map_register_table + " WHERE id = ?"
  869. else:
  870. sql = "DELETE FROM " + map_register_table + " WHERE id = %s"
  871. dbif.cursor.execute(sql, (self.base.get_id(),))
  872. # Remove the raster map from the space time raster dataset register
  873. if stds_register_table != None:
  874. if dbmi.paramstyle == "qmark":
  875. sql = "DELETE FROM " + stds_register_table + " WHERE id = ?"
  876. else:
  877. sql = "DELETE FROM " + stds_register_table + " WHERE id = %s"
  878. dbif.cursor.execute(sql, (map_id,))
  879. if connect == True:
  880. dbif.close()
  881. # decrease the counter
  882. self.map_counter -= 1
  883. def update_from_registered_maps(self, dbif = None):
  884. """This methods updates the spatial and temporal extent as well as
  885. type specific metadata. It should always been called after maps are registered
  886. or unregistered/deleted from the space time dataset.
  887. The update of the temporal extent checks if the end time is set correctly.
  888. In case the registered maps have no valid end time (None) the maximum start time
  889. will be used. If the end time is earlier than the maximum start time, it will
  890. be replaced by the maximum start time.
  891. An other solution to automate this is to use the deactivated trigger
  892. in the SQL files. But this will result in a huge performance issue
  893. in case many maps are registered (>1000).
  894. @param dbif: The database interface to be used
  895. """
  896. core.verbose(_("Update metadata, spatial and temporal extent from all registered maps of <%s>") % (self.get_id()))
  897. # Nothing to do if the register is not present
  898. if not self.get_map_register():
  899. return
  900. connect = False
  901. if dbif == None:
  902. dbif = sql_database_interface()
  903. dbif.connect()
  904. connect = True
  905. map_time = None
  906. use_start_time = False
  907. # Get basic info
  908. stds_name = self.base.get_name()
  909. stds_mapset = self.base.get_mapset()
  910. sql_path = get_sql_template_path()
  911. #We create a transaction
  912. sql_script = ""
  913. sql_script += "BEGIN TRANSACTION;\n"
  914. # Update the spatial and temporal extent from registered maps
  915. # Read the SQL template
  916. sql = open(os.path.join(sql_path, "update_stds_spatial_temporal_extent_template.sql"), 'r').read()
  917. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  918. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  919. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  920. sql = sql.replace("STDS", self.get_type())
  921. sql_script += sql
  922. sql_script += "\n"
  923. # Update type specific metadata
  924. sql = open(os.path.join(sql_path, "update_" + self.get_type() + "_metadata_template.sql"), 'r').read()
  925. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  926. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  927. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  928. sql = sql.replace("STDS", self.get_type())
  929. sql_script += sql
  930. sql_script += "\n"
  931. sql_script += "END TRANSACTION;"
  932. if dbmi.__name__ == "sqlite3":
  933. dbif.cursor.executescript(sql_script)
  934. else:
  935. dbif.cursor.execute(sql_script)
  936. # Read and validate the selected end time
  937. self.select()
  938. if self.is_time_absolute():
  939. start_time, end_time, tz = self.get_absolute_time()
  940. else:
  941. start_time, end_time, unit = self.get_relative_time()
  942. # In case no end time is set, use the maximum start time of all registered maps as end time
  943. if end_time == None:
  944. use_start_time = True
  945. else:
  946. # Check if the end time is smaller than the maximum start time
  947. if self.is_time_absolute():
  948. sql = """SELECT max(start_time) FROM GRASS_MAP_absolute_time WHERE GRASS_MAP_absolute_time.id IN
  949. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register);"""
  950. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  951. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  952. else:
  953. sql = """SELECT max(start_time) FROM GRASS_MAP_relative_time WHERE GRASS_MAP_relative_time.id IN
  954. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register);"""
  955. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  956. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  957. dbif.cursor.execute(sql)
  958. row = dbif.cursor.fetchone()
  959. if row != None:
  960. # This seems to be a bug in sqlite3 Python driver
  961. if dbmi.__name__ == "sqlite3":
  962. tstring = row[0]
  963. # Convert the unicode string into the datetime format
  964. if self.is_time_absolute():
  965. if tstring.find(":") > 0:
  966. time_format = "%Y-%m-%d %H:%M:%S"
  967. else:
  968. time_format = "%Y-%m-%d"
  969. max_start_time = datetime.strptime(tstring, time_format)
  970. else:
  971. max_start_time = row[0]
  972. else:
  973. max_start_time = row[0]
  974. if end_time < max_start_time:
  975. use_start_time = True
  976. # Set the maximum start time as end time
  977. if use_start_time:
  978. if self.is_time_absolute():
  979. sql = """UPDATE STDS_absolute_time SET end_time =
  980. (SELECT max(start_time) FROM GRASS_MAP_absolute_time WHERE GRASS_MAP_absolute_time.id IN
  981. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
  982. ) WHERE id = 'SPACETIME_ID';"""
  983. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  984. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  985. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  986. sql = sql.replace("STDS", self.get_type())
  987. elif self.is_time_relative():
  988. sql = """UPDATE STDS_relative_time SET end_time =
  989. (SELECT max(start_time) FROM GRASS_MAP_relative_time WHERE GRASS_MAP_relative_time.id IN
  990. (SELECT id FROM SPACETIME_NAME_GRASS_MAP_register)
  991. ) WHERE id = 'SPACETIME_ID';"""
  992. sql = sql.replace("GRASS_MAP", self.get_new_map_instance(None).get_type())
  993. sql = sql.replace("SPACETIME_NAME", stds_name + "_" + stds_mapset )
  994. sql = sql.replace("SPACETIME_ID", self.base.get_id())
  995. sql = sql.replace("STDS", self.get_type())
  996. if dbmi.__name__ == "sqlite3":
  997. dbif.cursor.executescript(sql)
  998. else:
  999. dbif.cursor.execute(sql)
  1000. # Count the temporal map types
  1001. maps = self.get_registered_maps_as_objects(dbif=dbif)
  1002. tlist = self.count_temporal_types(maps)
  1003. if tlist["interval"] > 0 and tlist["point"] == 0 and tlist["invalid"] == 0:
  1004. map_time = "interval"
  1005. elif tlist["interval"] == 0 and tlist["point"] > 0 and tlist["invalid"] == 0:
  1006. map_time = "point"
  1007. elif tlist["interval"] > 0 and tlist["point"] > 0 and tlist["invalid"] == 0:
  1008. map_time = "mixed"
  1009. else:
  1010. map_time = "invalid"
  1011. # Compute the granularity
  1012. if map_time != "invalid":
  1013. # Smallest supported temporal resolution
  1014. if self.is_time_absolute():
  1015. gran = compute_absolute_time_granularity(maps)
  1016. elif self.is_time_relative():
  1017. gran = compute_relative_time_granularity(maps)
  1018. else:
  1019. gran = None
  1020. # Set the map time type and update the time objects
  1021. if self.is_time_absolute():
  1022. self.absolute_time.select(dbif)
  1023. self.metadata.select(dbif)
  1024. if self.metadata.get_number_of_maps() > 0:
  1025. self.absolute_time.set_map_time(map_time)
  1026. self.absolute_time.set_granularity(gran)
  1027. else:
  1028. self.absolute_time.set_map_time(None)
  1029. self.absolute_time.set_granularity(None)
  1030. self.absolute_time.update_all(dbif)
  1031. else:
  1032. self.relative_time.select(dbif)
  1033. self.metadata.select(dbif)
  1034. if self.metadata.get_number_of_maps() > 0:
  1035. self.relative_time.set_map_time(map_time)
  1036. self.relative_time.set_granularity(gran)
  1037. else:
  1038. self.relative_time.set_map_time(None)
  1039. self.relative_time.set_granularity(None)
  1040. self.relative_time.update_all(dbif)
  1041. if connect == True:
  1042. dbif.close()
  1043. ###############################################################################
  1044. def create_temporal_relation_sql_where_statement(start, end, use_start=True, use_during=False,
  1045. use_overlap=False, use_contain=False, use_equal=False):
  1046. """ Create a SQL WHERE statement for temporal relation selection of maps in space time datasets
  1047. @param start: The start time
  1048. @param end: The end time
  1049. @param use_start: Select maps of which the start time is located in the selection granule
  1050. map : s
  1051. granule: s-----------------e
  1052. map : s--------------------e
  1053. granule: s-----------------e
  1054. map : s--------e
  1055. granule: s-----------------e
  1056. @param use_during: during: Select maps which are temporal during the selection granule
  1057. map : s-----------e
  1058. granule: s-----------------e
  1059. @param use_overlap: Select maps which temporal overlap the selection granule
  1060. map : s-----------e
  1061. granule: s-----------------e
  1062. map : s-----------e
  1063. granule: s----------e
  1064. @param use_contain: Select maps which temporally contain the selection granule
  1065. map : s-----------------e
  1066. granule: s-----------e
  1067. @param use_equal: Select maps which temporally equal to the selection granule
  1068. map : s-----------e
  1069. granule: s-----------e
  1070. """
  1071. where = "("
  1072. if use_start:
  1073. where += "(start_time >= '%s' and start_time < '%s') " % (start, end)
  1074. if use_during:
  1075. if use_start:
  1076. where += " OR "
  1077. where += "((start_time > '%s' and end_time < '%s') OR " % (start, end)
  1078. where += "(start_time >= '%s' and end_time < '%s') OR " % (start, end)
  1079. where += "(start_time > '%s' and end_time <= '%s'))" % (start, end)
  1080. if use_overlap:
  1081. if use_start or use_during:
  1082. where += " OR "
  1083. where += "((start_time < '%s' and end_time > '%s' and end_time < '%s') OR " % (start, start, end)
  1084. where += "(start_time < '%s' and start_time > '%s' and end_time > '%s'))" % (end, start, end)
  1085. if use_contain:
  1086. if use_start or use_during or use_overlap:
  1087. where += " OR "
  1088. where += "((start_time < '%s' and end_time > '%s') OR " % (start, end)
  1089. where += "(start_time <= '%s' and end_time > '%s') OR " % (start, end)
  1090. where += "(start_time < '%s' and end_time >= '%s'))" % (start, end)
  1091. if use_equal:
  1092. if use_start or use_during or use_overlap or use_contain:
  1093. where += " OR "
  1094. where += "(start_time = '%s' and end_time = '%s')" % (start, end)
  1095. where += ")"
  1096. return where