abstract_space_time_dataset.py 67 KB

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