abstract_space_time_dataset.py 62 KB

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