temporal_granularity.py 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211
  1. """
  2. Functions to compute the temporal granularity of a map list
  3. Usage:
  4. .. code-block:: python
  5. import grass.temporal as tgis
  6. tgis.compute_relative_time_granularity(maps)
  7. (C) 2012-2013 by the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. :authors: Soeren Gebbert
  12. """
  13. from __future__ import print_function
  14. # i18N
  15. import gettext
  16. from .datetime_math import *
  17. from .core import get_tgis_message_interface
  18. from functools import reduce
  19. from collections import OrderedDict
  20. import ast
  21. SINGULAR_GRAN = ["second", "minute", "hour", "day", "week", "month", "year"]
  22. PLURAL_GRAN = ["seconds", "minutes", "hours", "days", "weeks", "months",
  23. "years"]
  24. SUPPORTED_GRAN = SINGULAR_GRAN + PLURAL_GRAN
  25. CONVERT_GRAN = OrderedDict()
  26. CONVERT_GRAN['year'] = '12 month'
  27. CONVERT_GRAN['month'] = '30.436875 day'
  28. CONVERT_GRAN['day'] = '24 hour'
  29. CONVERT_GRAN['hour'] = '60 minute'
  30. CONVERT_GRAN['minute'] = '60 second'
  31. ###############################################################################
  32. def check_granularity_string(granularity, temporal_type):
  33. """Check if the granularity string is valid
  34. :param granularity: The granularity string
  35. :param temporal_type: The temporal type of the granularity relative or
  36. absolute
  37. :return: True if valid, False if invalid
  38. .. code-block:: python
  39. >>> check_granularity_string("1 year", "absolute")
  40. True
  41. >>> check_granularity_string("1 month", "absolute")
  42. True
  43. >>> check_granularity_string("1 day", "absolute")
  44. True
  45. >>> check_granularity_string("1 minute", "absolute")
  46. True
  47. >>> check_granularity_string("1 hour", "absolute")
  48. True
  49. >>> check_granularity_string("1 second", "absolute")
  50. True
  51. >>> check_granularity_string("5 months", "absolute")
  52. True
  53. >>> check_granularity_string("5 days", "absolute")
  54. True
  55. >>> check_granularity_string("5 minutes", "absolute")
  56. True
  57. >>> check_granularity_string("5 years", "absolute")
  58. True
  59. >>> check_granularity_string("5 hours", "absolute")
  60. True
  61. >>> check_granularity_string("2 seconds", "absolute")
  62. True
  63. >>> check_granularity_string("1 secondo", "absolute")
  64. False
  65. >>> check_granularity_string("bla second", "absolute")
  66. False
  67. >>> check_granularity_string("bla", "absolute")
  68. False
  69. >>> check_granularity_string(1, "relative")
  70. True
  71. >>> check_granularity_string("bla", "relative")
  72. False
  73. """
  74. temporal_type
  75. if granularity is None:
  76. return False
  77. if temporal_type == "absolute":
  78. try:
  79. num, unit = granularity.split(" ")
  80. except:
  81. return False
  82. if unit not in SUPPORTED_GRAN:
  83. return False
  84. try:
  85. integer = int(num)
  86. except:
  87. return False
  88. elif temporal_type == "relative":
  89. try:
  90. integer = int(granularity)
  91. except:
  92. return False
  93. else:
  94. return False
  95. return True
  96. ###############################################################################
  97. def compute_relative_time_granularity(maps):
  98. """Compute the relative time granularity
  99. Attention: The computation of the granularity
  100. is only correct in case of not overlapping intervals.
  101. Hence a correct temporal topology is required for computation.
  102. :param maps: a ordered by start_time list of map objects
  103. :return: An integer
  104. .. code-block:: python
  105. >>> import grass.temporal as tgis
  106. >>> tgis.init()
  107. >>> maps = []
  108. >>> for i in range(5):
  109. ... map = tgis.RasterDataset("a%i@P"%i)
  110. ... check = map.set_relative_time(i,i + 1,"seconds")
  111. ... if check:
  112. ... maps.append(map)
  113. >>> tgis.compute_relative_time_granularity(maps)
  114. 1
  115. >>> maps = []
  116. >>> count = 0
  117. >>> timelist = ((0,3), (3,6), (6,9))
  118. >>> for t in timelist:
  119. ... map = tgis.RasterDataset("a%i@P"%count)
  120. ... check = map.set_relative_time(t[0],t[1],"years")
  121. ... if check:
  122. ... maps.append(map)
  123. ... count += 1
  124. >>> tgis.compute_relative_time_granularity(maps)
  125. 3
  126. >>> maps = []
  127. >>> count = 0
  128. >>> timelist = ((0,3), (4,6), (8,11))
  129. >>> for t in timelist:
  130. ... map = tgis.RasterDataset("a%i@P"%count)
  131. ... check = map.set_relative_time(t[0],t[1],"years")
  132. ... if check:
  133. ... maps.append(map)
  134. ... count += 1
  135. >>> tgis.compute_relative_time_granularity(maps)
  136. 1
  137. >>> maps = []
  138. >>> count = 0
  139. >>> timelist = ((0,8), (2,6), (5,9))
  140. >>> for t in timelist:
  141. ... map = tgis.RasterDataset("a%i@P"%count)
  142. ... check = map.set_relative_time(t[0],t[1],"months")
  143. ... if check:
  144. ... maps.append(map)
  145. ... count += 1
  146. >>> tgis.compute_relative_time_granularity(maps)
  147. 4
  148. >>> maps = []
  149. >>> count = 0
  150. >>> timelist = ((0,8), (8,12), (12,18))
  151. >>> for t in timelist:
  152. ... map = tgis.RasterDataset("a%i@P"%count)
  153. ... check = map.set_relative_time(t[0],t[1],"days")
  154. ... if check:
  155. ... maps.append(map)
  156. ... count += 1
  157. >>> tgis.compute_relative_time_granularity(maps)
  158. 2
  159. >>> maps = []
  160. >>> count = 0
  161. >>> timelist = ((0,None), (8,None), (12,None), (24,None))
  162. >>> for t in timelist:
  163. ... map = tgis.RasterDataset("a%i@P"%count)
  164. ... check = map.set_relative_time(t[0],t[1],"minutes")
  165. ... if check:
  166. ... maps.append(map)
  167. ... count += 1
  168. >>> tgis.compute_relative_time_granularity(maps)
  169. 4
  170. >>> maps = []
  171. >>> count = 0
  172. >>> timelist = ((0,None), (8,14), (18,None), (24,None))
  173. >>> for t in timelist:
  174. ... map = tgis.RasterDataset("a%i@P"%count)
  175. ... check = map.set_relative_time(t[0],t[1],"hours")
  176. ... if check:
  177. ... maps.append(map)
  178. ... count += 1
  179. >>> tgis.compute_relative_time_granularity(maps)
  180. 2
  181. >>> maps = []
  182. >>> count = 0
  183. >>> timelist = ((0,21),)
  184. >>> for t in timelist:
  185. ... map = tgis.RasterDataset("a%i@P"%count)
  186. ... check = map.set_relative_time(t[0],t[1],"hours")
  187. ... if check:
  188. ... maps.append(map)
  189. ... count += 1
  190. >>> tgis.compute_relative_time_granularity(maps)
  191. 21
  192. """
  193. # The interval time must be scaled to days resolution
  194. granularity = None
  195. delta = []
  196. # First we compute the timedelta of the intervals
  197. for map in maps:
  198. start, end = map.get_temporal_extent_as_tuple()
  199. if (start == 0 or start) and end:
  200. t = abs(end - start)
  201. delta.append(int(t))
  202. # Compute the timedelta of the gaps
  203. for i in range(len(maps)):
  204. if i < len(maps) - 1:
  205. relation = maps[i + 1].temporal_relation(maps[i])
  206. if relation == "after":
  207. start1, end1 = maps[i].get_temporal_extent_as_tuple()
  208. start2, end2 = maps[i + 1].get_temporal_extent_as_tuple()
  209. # Gaps are between intervals, intervals and
  210. # points, points and points
  211. if end1 and start2:
  212. t = abs(end1 - start2)
  213. delta.append(int(t))
  214. if not end1 and start2:
  215. t = abs(start1 - start2)
  216. delta.append(int(t))
  217. delta.sort()
  218. ulist = list(set(delta))
  219. if len(ulist) > 1:
  220. # Find greatest common divisor
  221. granularity = gcd_list(ulist)
  222. elif len(ulist) == 1:
  223. granularity = ulist[0]
  224. else:
  225. granularity = 0
  226. return granularity
  227. ###############################################################################
  228. def compute_absolute_time_granularity(maps):
  229. """Compute the absolute time granularity
  230. Attention: The computation of the granularity
  231. is only correct in case of not overlapping intervals.
  232. Hence a correct temporal topology is required for computation.
  233. The computed granularity is returned as number of seconds or minutes
  234. or hours or days or months or years.
  235. :param maps: a ordered by start_time list of map objects
  236. :return: The temporal topology as string "integer unit"
  237. .. code-block:: python
  238. >>> import grass.temporal as tgis
  239. >>> import datetime
  240. >>> dt = datetime.datetime
  241. >>> tgis.init()
  242. >>> maps = []
  243. >>> count = 0
  244. >>> timelist = ((dt(2000,01,01),None), (dt(2000,02,01),None))
  245. >>> for t in timelist:
  246. ... map = tgis.RasterDataset("a%i@P"%count)
  247. ... check = map.set_absolute_time(t[0],t[1])
  248. ... if check:
  249. ... maps.append(map)
  250. ... count += 1
  251. >>> tgis.compute_absolute_time_granularity(maps)
  252. '1 month'
  253. >>> maps = []
  254. >>> count = 0
  255. >>> timelist = ((dt(2000,01,01),None), (dt(2000,01,02),None), (dt(2000,01,03),None))
  256. >>> for t in timelist:
  257. ... map = tgis.RasterDataset("a%i@P"%count)
  258. ... check = map.set_absolute_time(t[0],t[1])
  259. ... if check:
  260. ... maps.append(map)
  261. ... count += 1
  262. >>> tgis.compute_absolute_time_granularity(maps)
  263. '1 day'
  264. >>> maps = []
  265. >>> count = 0
  266. >>> timelist = ((dt(2000,01,01),None), (dt(2000,01,02),None), (dt(2000,05,04,0,5,30),None))
  267. >>> for t in timelist:
  268. ... map = tgis.RasterDataset("a%i@P"%count)
  269. ... check = map.set_absolute_time(t[0],t[1])
  270. ... if check:
  271. ... maps.append(map)
  272. ... count += 1
  273. >>> tgis.compute_absolute_time_granularity(maps)
  274. '30 seconds'
  275. >>> maps = []
  276. >>> count = 0
  277. >>> timelist = ((dt(2000,01,01),dt(2000,05,02)), (dt(2000,05,04,2),None))
  278. >>> for t in timelist:
  279. ... map = tgis.RasterDataset("a%i@P"%count)
  280. ... check = map.set_absolute_time(t[0],t[1])
  281. ... if check:
  282. ... maps.append(map)
  283. ... count += 1
  284. >>> tgis.compute_absolute_time_granularity(maps)
  285. '2 hours'
  286. >>> maps = []
  287. >>> count = 0
  288. >>> timelist = ((dt(2000,01,01),dt(2000,02,01)), (dt(2005,05,04,12),dt(2007,05,20,6)))
  289. >>> for t in timelist:
  290. ... map = tgis.RasterDataset("a%i@P"%count)
  291. ... check = map.set_absolute_time(t[0],t[1])
  292. ... if check:
  293. ... maps.append(map)
  294. ... count += 1
  295. >>> tgis.compute_absolute_time_granularity(maps)
  296. '6 hours'
  297. """
  298. has_seconds = False
  299. has_minutes = False
  300. has_hours = False
  301. has_days = False
  302. has_months = False
  303. has_years = False
  304. use_seconds = False
  305. use_minutes = False
  306. use_hours = False
  307. use_days = False
  308. use_months = False
  309. use_years = False
  310. delta = []
  311. datetime_delta = []
  312. # First we compute the timedelta of the intervals
  313. for map in maps:
  314. start, end = map.get_temporal_extent_as_tuple()
  315. if start and end:
  316. delta.append(end - start)
  317. datetime_delta.append(compute_datetime_delta(start, end))
  318. # Compute the timedelta of the gaps
  319. for i in range(len(maps)):
  320. if i < len(maps) - 1:
  321. relation = maps[i + 1].temporal_relation(maps[i])
  322. if relation == "after":
  323. start1, end1 = maps[i].get_temporal_extent_as_tuple()
  324. start2, end2 = maps[i + 1].get_temporal_extent_as_tuple()
  325. # Gaps are between intervals, intervals and
  326. # points, points and points
  327. if end1 and start2:
  328. delta.append(end1 - start2)
  329. datetime_delta.append(compute_datetime_delta(end1, start2))
  330. if not end1 and start2:
  331. delta.append(start2 - start1)
  332. datetime_delta.append(compute_datetime_delta(
  333. start1, start2))
  334. # Check what changed
  335. dlist = []
  336. for d in datetime_delta:
  337. if "second" in d and d["second"] > 0:
  338. has_seconds = True
  339. #print "has second"
  340. if "minute" in d and d["minute"] > 0:
  341. has_minutes = True
  342. #print "has minute"
  343. if "hour" in d and d["hour"] > 0:
  344. has_hours = True
  345. #print "has hour"
  346. if "day" in d and d["day"] > 0:
  347. has_days = True
  348. #print "has day"
  349. if "month" in d and d["month"] > 0:
  350. has_months = True
  351. #print "has month"
  352. if "year" in d and d["year"] > 0:
  353. has_years = True
  354. #print "has year"
  355. # Create a list with a single time unit only
  356. if has_seconds:
  357. for d in datetime_delta:
  358. if "second" in d and d["second"] > 0:
  359. dlist.append(d["second"])
  360. elif "minute" in d and d["minute"] > 0:
  361. dlist.append(d["minute"] * 60)
  362. elif "hour" in d and d["hour"] > 0:
  363. dlist.append(d["hour"] * 3600)
  364. elif "day" in d and d["day"] > 0:
  365. dlist.append(d["day"] * 24 * 3600)
  366. else:
  367. dlist.append(d["max_days"] * 24 * 3600)
  368. use_seconds = True
  369. elif has_minutes:
  370. for d in datetime_delta:
  371. if "minute" in d and d["minute"] > 0:
  372. dlist.append(d["minute"])
  373. elif "hour" in d and d["hour"] > 0:
  374. dlist.append(d["hour"] * 60)
  375. elif "day" in d:
  376. dlist.append(d["day"] * 24 * 60)
  377. else:
  378. dlist.append(d["max_days"] * 24 * 60)
  379. use_minutes = True
  380. elif has_hours:
  381. for d in datetime_delta:
  382. if "hour" in d and d["hour"] > 0:
  383. dlist.append(d["hour"])
  384. elif "day" in d and d["day"] > 0:
  385. dlist.append(d["day"] * 24)
  386. else:
  387. dlist.append(d["max_days"] * 24)
  388. use_hours = True
  389. elif has_days:
  390. for d in datetime_delta:
  391. if "day" in d and d["day"] > 0:
  392. dlist.append(d["day"])
  393. else:
  394. dlist.append(d["max_days"])
  395. use_days = True
  396. elif has_months:
  397. for d in datetime_delta:
  398. if "month" in d and d["month"] > 0:
  399. dlist.append(d["month"])
  400. elif "year" in d and d["year"] > 0:
  401. dlist.append(d["year"] * 12)
  402. use_months = True
  403. elif has_years:
  404. for d in datetime_delta:
  405. if "year" in d:
  406. dlist.append(d["year"])
  407. use_years = True
  408. dlist.sort()
  409. ulist = list(set(dlist))
  410. if len(ulist) == 0:
  411. return None
  412. if len(ulist) > 1:
  413. # Find greatest common divisor
  414. granularity = gcd_list(ulist)
  415. else:
  416. granularity = ulist[0]
  417. if use_seconds:
  418. if granularity == 1:
  419. return "%i second" % granularity
  420. else:
  421. return "%i seconds" % granularity
  422. elif use_minutes:
  423. if granularity == 1:
  424. return "%i minute" % granularity
  425. else:
  426. return "%i minutes" % granularity
  427. elif use_hours:
  428. if granularity == 1:
  429. return "%i hour" % granularity
  430. else:
  431. return "%i hours" % granularity
  432. elif use_days:
  433. if granularity == 1:
  434. return "%i day" % granularity
  435. else:
  436. return "%i days" % granularity
  437. elif use_months:
  438. if granularity == 1:
  439. return "%i month" % granularity
  440. else:
  441. return "%i months" % granularity
  442. elif use_years:
  443. if granularity == 1:
  444. return "%i year" % granularity
  445. else:
  446. return "%i years" % granularity
  447. return None
  448. ###############################################################################
  449. def compute_common_relative_time_granularity(gran_list):
  450. """Compute the greatest common granule from a list of relative time granules
  451. .. code-block:: python
  452. >>> import grass.temporal as tgis
  453. >>> tgis.init()
  454. >>> grans = [1,2,30]
  455. >>> tgis.compute_common_relative_time_granularity(grans)
  456. 1
  457. >>> import grass.temporal as tgis
  458. >>> tgis.init()
  459. >>> grans = [10,20,30]
  460. >>> tgis.compute_common_relative_time_granularity(grans)
  461. 10
  462. """
  463. return gcd_list(gran_list)
  464. ###############################################################################
  465. def compute_common_absolute_time_granularity(gran_list,
  466. start_date_list = None):
  467. """ Compute the greatest common granule from a list of absolute time granules,
  468. considering the start times of the related space time datasets in the
  469. common granularity computation.
  470. The list of start dates is optional. If you use this function to compute a common
  471. granularity between space time datasets, then you should provide their start times
  472. to avoid wrong synchronization.
  473. :param gran_list: List of granularities
  474. :param start_date_list: List of the start times of related space time datasets
  475. :return: The common granularity
  476. .. code-block:: python
  477. >>> from datetime import datetime
  478. >>> import grass.temporal as tgis
  479. >>> tgis.init()
  480. >>> grans = ["20 second", "10 minutes", "2 hours"]
  481. >>> dates = [datetime(2001,1,1,0,0,0),
  482. ... datetime(2001,1,1,0,0,0),
  483. ... datetime(2001,1,1,0,0,0),]
  484. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  485. '20 seconds'
  486. >>> grans = ["20 second", "10 minutes", "2 hours"]
  487. >>> dates = [datetime(2001,1,1,0,0,20),
  488. ... datetime(2001,1,1,0,0,0),
  489. ... datetime(2001,1,1,0,0,0),]
  490. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  491. '1 second'
  492. >>> grans = ["7200 second", "240 minutes", "1 year"]
  493. >>> dates = [datetime(2001,1,1,0,0,10),
  494. ... datetime(2001,1,1,0,0,0),
  495. ... datetime(2001,1,1,0,0,0),]
  496. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  497. '1 second'
  498. >>> grans = ["7200 second", "89 minutes", "1 year"]
  499. >>> dates = [datetime(2001,1,1,0,0,0),
  500. ... datetime(2001,1,1,0,0,0),
  501. ... datetime(2001,1,1,0,0,0),]
  502. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  503. '60 seconds'
  504. >>> grans = ["120 minutes", "2 hours"]
  505. >>> dates = [datetime(2001,1,1,0,0,0),
  506. ... datetime(2001,1,1,0,0,0),]
  507. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  508. '60 minutes'
  509. >>> grans = ["120 minutes", "2 hours"]
  510. >>> dates = [datetime(2001,1,1,0,30,0),
  511. ... datetime(2001,1,1,0,0,0),]
  512. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  513. '1 minute'
  514. >>> grans = ["360 minutes", "3 hours"]
  515. >>> dates = [datetime(2001,1,1,0,0,0),
  516. ... datetime(2001,1,1,0,0,0),]
  517. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  518. '60 minutes'
  519. >>> grans = ["2 hours", "4 hours", "8 hours"]
  520. >>> dates = [datetime(2001,1,1,0,0,0),
  521. ... datetime(2001,1,1,0,0,0),
  522. ... datetime(2001,1,1,0,0,0),]
  523. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  524. '2 hours'
  525. >>> grans = ["2 hours", "4 hours", "8 hours"]
  526. >>> dates = [datetime(2001,1,1,2,0,0),
  527. ... datetime(2001,1,1,4,0,0),
  528. ... datetime(2001,1,1,8,0,0),]
  529. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  530. '1 hour'
  531. >>> grans = ["8 hours", "2 days"]
  532. >>> dates = [datetime(2001,1,1,0,0,0),
  533. ... datetime(2001,1,1,0,0,0),]
  534. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  535. '8 hours'
  536. >>> grans = ["8 hours", "2 days"]
  537. >>> dates = [datetime(2001,1,1,10,0,0),
  538. ... datetime(2001,1,1,0,0,0),]
  539. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  540. '1 hour'
  541. >>> grans = ["120 months", "360 months", "4 years"]
  542. >>> dates = [datetime(2001,1,1,0,0,0),
  543. ... datetime(2001,1,1,0,0,0),
  544. ... datetime(2001,1,1,0,0,0),]
  545. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  546. '12 months'
  547. >>> grans = ["30 days", "10 days", "5 days"]
  548. >>> dates = [datetime(2001,2,1,0,0,0),
  549. ... datetime(2001,1,1,0,0,0),
  550. ... datetime(2001,1,1,0,0,0),]
  551. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  552. '5 days'
  553. >>> grans = ["30 days", "10 days", "5 days"]
  554. >>> dates = [datetime(2001,2,2,0,0,0),
  555. ... datetime(2001,1,1,0,0,0),
  556. ... datetime(2001,1,1,0,0,0),]
  557. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  558. '1 day'
  559. >>> grans = ["2 days", "360 months", "4 years"]
  560. >>> dates = [datetime(2001,1,1,0,0,0),
  561. ... datetime(2001,1,1,0,0,0),
  562. ... datetime(2001,1,1,0,0,0),]
  563. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  564. '2 days'
  565. >>> grans = ["2 days", "360 months", "4 years"]
  566. >>> dates = [datetime(2001,1,2,0,0,0),
  567. ... datetime(2001,1,1,0,0,0),
  568. ... datetime(2001,1,1,0,0,0),]
  569. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  570. '1 day'
  571. >>> grans = ["120 months", "360 months", "4 years"]
  572. >>> dates = [datetime(2001,2,1,0,0,0),
  573. ... datetime(2001,1,1,0,0,0),
  574. ... datetime(2001,1,1,0,0,0),]
  575. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  576. '1 month'
  577. >>> grans = ["120 months", "361 months", "4 years"]
  578. >>> dates = [datetime(2001,1,1,0,0,0),
  579. ... datetime(2001,1,1,0,0,0),
  580. ... datetime(2001,1,1,0,0,0),]
  581. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  582. '1 month'
  583. >>> grans = ["120 months", "360 months", "4 years"]
  584. >>> dates = [datetime(2001,1,1,0,0,0),
  585. ... datetime(2001,1,1,0,0,0),
  586. ... datetime(2001,1,1,0,0,0),]
  587. >>> tgis.compute_common_absolute_time_granularity(grans, dates)
  588. '12 months'
  589. ..
  590. """
  591. common_granule = compute_common_absolute_time_granularity_simple(gran_list)
  592. if start_date_list is None:
  593. return common_granule
  594. num, granule = common_granule.split()
  595. if granule in ["seconds", "second"]:
  596. # If the start seconds are different between the start dates
  597. # set the granularity to one second
  598. for start_time in start_date_list:
  599. if start_time.second != start_date_list[0].second:
  600. return "1 second"
  601. # Make sure the granule does not exceed the hierarchy limit
  602. if int(num) > 60:
  603. if int(num)%60 == 0:
  604. return "60 seconds"
  605. else:
  606. return "1 second"
  607. if granule in ["minutes", "minute"]:
  608. # If the start minutes are different between the start dates
  609. # set the granularity to one minute
  610. for start_time in start_date_list:
  611. if start_time.minute != start_date_list[0].minute:
  612. return "1 minute"
  613. # Make sure the granule does not exceed the hierarchy limit
  614. if int(num) > 60:
  615. if int(num)%60 == 0:
  616. return "60 minutes"
  617. else:
  618. return "1 minute"
  619. if granule in ["hours", "hour"]:
  620. # If the start hours are different between the start dates
  621. # set the granularity to one hour
  622. for start_time in start_date_list:
  623. if start_time.hour != start_date_list[0].hour:
  624. return "1 hour"
  625. # Make sure the granule does not exceed the hierarchy limit
  626. if int(num) > 24:
  627. if int(num)%24 == 0:
  628. return "24 hours"
  629. else:
  630. return "1 hour"
  631. if granule in ["days", "day"]:
  632. # If the start days are different between the start dates
  633. # set the granularity to one day
  634. for start_time in start_date_list:
  635. if start_time.day != start_date_list[0].day:
  636. return "1 day"
  637. # Make sure the granule does not exceed the hierarchy limit
  638. if int(num) > 365:
  639. if int(num)%365 == 0:
  640. return "365 days"
  641. else:
  642. return "1 day"
  643. if granule in ["months", "month"]:
  644. # If the start months are different between the start dates
  645. # set the granularity to one month
  646. for start_time in start_date_list:
  647. if start_time.month != start_date_list[0].month:
  648. return "1 month"
  649. # Make sure the granule does not exceed the hierarchy limit
  650. if int(num) > 12:
  651. if int(num)%12 == 0:
  652. return "12 months"
  653. else:
  654. return "1 month"
  655. return common_granule
  656. ###############################################################################
  657. def compute_common_absolute_time_granularity_simple(gran_list):
  658. """ Compute the greatest common granule from a list of absolute time granules
  659. :param gran_list: List of granularities
  660. :return: The common granularity
  661. .. code-block:: python
  662. >>> import grass.temporal as tgis
  663. >>> tgis.init()
  664. >>> grans = ["1 second", "2 seconds", "30 seconds"]
  665. >>> tgis.compute_common_absolute_time_granularity(grans)
  666. '1 second'
  667. >>> grans = ["3 second", "6 seconds", "30 seconds"]
  668. >>> tgis.compute_common_absolute_time_granularity(grans)
  669. '3 seconds'
  670. >>> grans = ["12 second", "18 seconds", "30 seconds", "10 minutes"]
  671. >>> tgis.compute_common_absolute_time_granularity(grans)
  672. '6 seconds'
  673. >>> grans = ["20 second", "10 minutes", "2 hours"]
  674. >>> tgis.compute_common_absolute_time_granularity(grans)
  675. '20 seconds'
  676. >>> grans = ["7200 second", "240 minutes", "1 year"]
  677. >>> tgis.compute_common_absolute_time_granularity(grans)
  678. '7200 seconds'
  679. >>> grans = ["7200 second", "89 minutes", "1 year"]
  680. >>> tgis.compute_common_absolute_time_granularity(grans)
  681. '60 seconds'
  682. >>> grans = ["10 minutes", "20 minutes", "30 minutes", "40 minutes", "2 hours"]
  683. >>> tgis.compute_common_absolute_time_granularity(grans)
  684. '10 minutes'
  685. >>> grans = ["120 minutes", "2 hours"]
  686. >>> tgis.compute_common_absolute_time_granularity(grans)
  687. '120 minutes'
  688. >>> grans = ["360 minutes", "3 hours"]
  689. >>> tgis.compute_common_absolute_time_granularity(grans)
  690. '180 minutes'
  691. >>> grans = ["2 hours", "4 hours", "8 hours"]
  692. >>> tgis.compute_common_absolute_time_granularity(grans)
  693. '2 hours'
  694. >>> grans = ["8 hours", "2 days"]
  695. >>> tgis.compute_common_absolute_time_granularity(grans)
  696. '8 hours'
  697. >>> grans = ["48 hours", "1 month"]
  698. >>> tgis.compute_common_absolute_time_granularity(grans)
  699. '24 hours'
  700. >>> grans = ["48 hours", "1 year"]
  701. >>> tgis.compute_common_absolute_time_granularity(grans)
  702. '24 hours'
  703. >>> grans = ["2 months", "4 months", "1 year"]
  704. >>> tgis.compute_common_absolute_time_granularity(grans)
  705. '2 months'
  706. >>> grans = ["120 months", "360 months", "4 years"]
  707. >>> tgis.compute_common_absolute_time_granularity(grans)
  708. '24 months'
  709. >>> grans = ["120 months", "361 months", "4 years"]
  710. >>> tgis.compute_common_absolute_time_granularity(grans)
  711. '1 month'
  712. >>> grans = ["2 years", "3 years", "4 years"]
  713. >>> tgis.compute_common_absolute_time_granularity(grans)
  714. '1 year'
  715. """
  716. has_seconds = False # 0
  717. has_minutes = False # 1
  718. has_hours = False # 2
  719. has_days = False # 3
  720. has_months = False # 4
  721. has_years = False # 5
  722. seconds = []
  723. minutes = []
  724. hours = []
  725. days = []
  726. months = []
  727. years = []
  728. min_gran = 6
  729. max_gran = -1
  730. for entry in gran_list:
  731. if not check_granularity_string(entry, "absolute"):
  732. return False
  733. num, gran = entry.split()
  734. if gran in ["seconds", "second"]:
  735. has_seconds = True
  736. if min_gran > 0:
  737. min_gran = 0
  738. if max_gran < 0:
  739. max_gran = 0
  740. seconds.append(int(num))
  741. if gran in ["minutes", "minute"]:
  742. has_minutes = True
  743. if min_gran > 1:
  744. min_gran = 1
  745. if max_gran < 1:
  746. max_gran = 1
  747. minutes.append(int(num))
  748. if gran in ["hours", "hour"]:
  749. has_hours = True
  750. if min_gran > 2:
  751. min_gran = 2
  752. if max_gran < 2:
  753. max_gran = 2
  754. hours.append(int(num))
  755. if gran in ["days", "day"]:
  756. has_days = True
  757. if min_gran > 3:
  758. min_gran = 3
  759. if max_gran < 3:
  760. max_gran = 3
  761. days.append(int(num))
  762. if gran in ["months", "month"]:
  763. has_months = True
  764. if min_gran > 4:
  765. min_gran = 4
  766. if max_gran < 4:
  767. max_gran = 4
  768. months.append(int(num))
  769. if gran in ["years", "year"]:
  770. has_years = True
  771. if min_gran > 5:
  772. min_gran = 5
  773. if max_gran < 5:
  774. max_gran = 5
  775. years.append(int(num))
  776. if has_seconds:
  777. if has_minutes:
  778. minutes.sort()
  779. seconds.append(minutes[0]*60)
  780. if has_hours:
  781. hours.sort()
  782. seconds.append(hours[0]*60*60)
  783. if has_days:
  784. days.sort()
  785. seconds.append(days[0]*60*60*24)
  786. if has_months:
  787. months.sort()
  788. seconds.append(months[0]*60*60*24*28)
  789. seconds.append(months[0]*60*60*24*29)
  790. seconds.append(months[0]*60*60*24*30)
  791. seconds.append(months[0]*60*60*24*31)
  792. if has_years:
  793. years.sort()
  794. seconds.append(years[0]*60*60*24*365)
  795. seconds.append(years[0]*60*60*24*366)
  796. num = gcd_list(seconds)
  797. gran = "second"
  798. if num > 1:
  799. gran += "s"
  800. return "%i %s"%(num, gran)
  801. elif has_minutes:
  802. if has_hours:
  803. hours.sort()
  804. minutes.append(hours[0]*60)
  805. if has_days:
  806. days.sort()
  807. minutes.append(days[0]*60*24)
  808. if has_months:
  809. months.sort()
  810. minutes.append(months[0]*60*24*28)
  811. minutes.append(months[0]*60*24*29)
  812. minutes.append(months[0]*60*24*30)
  813. minutes.append(months[0]*60*24*31)
  814. if has_years:
  815. years.sort()
  816. minutes.append(years[0]*60*24*365)
  817. minutes.append(years[0]*60*24*366)
  818. num = gcd_list(minutes)
  819. gran = "minute"
  820. if num > 1:
  821. gran += "s"
  822. return "%i %s"%(num, gran)
  823. elif has_hours:
  824. if has_days:
  825. days.sort()
  826. hours.append(days[0]*24)
  827. if has_months:
  828. months.sort()
  829. hours.append(months[0]*24*28)
  830. hours.append(months[0]*24*29)
  831. hours.append(months[0]*24*30)
  832. hours.append(months[0]*24*31)
  833. if has_years:
  834. years.sort()
  835. hours.append(years[0]*24*365)
  836. hours.append(years[0]*24*366)
  837. num = gcd_list(hours)
  838. gran = "hour"
  839. if num > 1:
  840. gran += "s"
  841. return "%i %s"%(num, gran)
  842. elif has_days:
  843. if has_months:
  844. months.sort()
  845. days.append(months[0]*28)
  846. days.append(months[0]*29)
  847. days.append(months[0]*30)
  848. days.append(months[0]*31)
  849. if has_years:
  850. years.sort()
  851. days.append(years[0]*365)
  852. days.append(years[0]*366)
  853. num = gcd_list(days)
  854. gran = "day"
  855. if num > 1:
  856. gran += "s"
  857. return "%i %s"%(num, gran)
  858. elif has_months:
  859. if has_years:
  860. years.sort()
  861. months.append(years[0]*12)
  862. num = gcd_list(months)
  863. gran = "month"
  864. if num > 1:
  865. gran += "s"
  866. return "%i %s"%(num, gran)
  867. elif has_years:
  868. num = gcd_list(years)
  869. gran = "year"
  870. if num > 1:
  871. gran += "s"
  872. return "%i %s"%(num, gran)
  873. #######################################################################
  874. def gran_singular_unit(gran):
  875. """Return the absolute granularity unit in its singular term
  876. :param gran: input granularity
  877. :return: granularity unit
  878. .. code-block:: python
  879. >>> import grass.temporal as tgis
  880. >>> tgis.init()
  881. >>> tgis.gran_singular_unit('1 month')
  882. 'month'
  883. >>> tgis.gran_singular_unit('2 months')
  884. 'month'
  885. >>> tgis.gran_singular_unit('6 seconds')
  886. 'second'
  887. >>> tgis.gran_singular_unit('1 year')
  888. 'year'
  889. """
  890. if check_granularity_string(gran, 'absolute'):
  891. output, unit = gran.split(" ")
  892. if unit in PLURAL_GRAN:
  893. return unit[:-1]
  894. elif unit in SINGULAR_GRAN:
  895. return unit
  896. else:
  897. lists = "{gr}".format(gr=SUPPORTED_GRAN).replace('[',
  898. '').replace(']',
  899. '')
  900. print(_("Output granularity seems not to be valid. Please use "
  901. "one of the following values : {gr}".format(gr=lists)))
  902. return False
  903. else:
  904. print(_("Invalid absolute granularity"))
  905. return False
  906. #######################################################################
  907. def gran_plural_unit(gran):
  908. """Return the absolute granularity unit in its singular term
  909. :param gran: input granularity
  910. :return: granularity unit
  911. .. code-block:: python
  912. >>> import grass.temporal as tgis
  913. >>> tgis.init()
  914. >>> tgis.gran_singular_unit('1 month')
  915. 'month'
  916. >>> tgis.gran_singular_unit('2 months')
  917. 'month'
  918. >>> tgis.gran_singular_unit('6 seconds')
  919. 'second'
  920. >>> tgis.gran_singular_unit('1 year')
  921. 'year'
  922. """
  923. if check_granularity_string(gran, 'absolute'):
  924. output, unit = gran.split(" ")
  925. if unit in PLURAL_GRAN:
  926. return unit
  927. elif unit in SINGULAR_GRAN:
  928. return "{gr}s".format(gr=unit)
  929. else:
  930. lists = "{gr}".format(gr=SUPPORTED_GRAN).replace('[',
  931. '').replace(']',
  932. '')
  933. print(_("Output granularity seems not to be valid. Please use "
  934. "one of the following values : {gr}".format(gr=lists)))
  935. else:
  936. print(_("Invalid absolute granularity"))
  937. return False
  938. ########################################################################
  939. def gran_to_gran(from_gran, to_gran="days", shell=False):
  940. """Converts the computed absolute granularity of a STDS to a smaller
  941. granularity based on the Gregorian calendar hierarchy that 1 year
  942. equals 12 months or 365.2425 days or 24 * 365.2425 hours or 86400 *
  943. 365.2425 seconds.
  944. :param from_gran: input granularity, this should be bigger than to_gran
  945. :param to_gran: output granularity
  946. :return: The output granularity
  947. .. code-block:: python
  948. >>> import grass.temporal as tgis
  949. >>> tgis.init()
  950. >>> tgis.gran_to_gran('1 month', '1 day')
  951. '30.436875 days'
  952. >>> tgis.gran_to_gran('1 month', '1 day', True)
  953. 30.436875
  954. >>> tgis.gran_to_gran('10 year', '1 hour')
  955. '87658.2 hours'
  956. >>> tgis.gran_to_gran('10 year', '1 minute')
  957. '5259492.0 minutes'
  958. >>> tgis.gran_to_gran('6 months', '1 day')
  959. '182.62125 days'
  960. >>> tgis.gran_to_gran('1 months', '1 second')
  961. '2629746.0 seconds'
  962. >>> tgis.gran_to_gran('1 month', '1 second', True)
  963. 2629746.0
  964. >>> tgis.gran_to_gran('30 month', '1 month', True)
  965. 30
  966. """
  967. def _return(output, tounit, shell):
  968. """Fuction to return the output"""
  969. if shell:
  970. return output
  971. else:
  972. if output == 1:
  973. return "{val} {unit}".format(val=output, unit=tounit)
  974. else:
  975. return "{val} {unit}s".format(val=output, unit=tounit)
  976. #TODO check the leap second
  977. if check_granularity_string(from_gran, 'absolute'):
  978. output, unit = from_gran.split(" ")
  979. if unit in PLURAL_GRAN:
  980. unit = unit[:-1]
  981. myunit = unit
  982. tounit = gran_singular_unit(to_gran)
  983. output = ast.literal_eval(output)
  984. for k, v in CONVERT_GRAN.items():
  985. if myunit == tounit:
  986. return _return(output, tounit, shell)
  987. if k == myunit:
  988. num, myunit = v.split(" ")
  989. output = output * ast.literal_eval(num)
  990. if tounit == 'second' and myunit == tounit:
  991. return _return(output, tounit, shell)
  992. print(_("Probably you need to invert 'from_gran' and 'to_gran'"))
  993. return False
  994. else:
  995. print(_("Invalid absolute granularity"))
  996. return False
  997. ###############################################################################
  998. # http://akiscode.com/articles/gcd_of_a_list.shtml
  999. # Copyright (c) 2010 Stephen Akiki
  1000. # MIT License (Means you can do whatever you want with this)
  1001. # See http://www.opensource.org/licenses/mit-license.php
  1002. # Error Codes:
  1003. # None
  1004. def gcd(a, b):
  1005. """The Euclidean Algorithm """
  1006. a = abs(a)
  1007. b = abs(b)
  1008. while a:
  1009. a, b = b % a, a
  1010. return b
  1011. ###############################################################################
  1012. def gcd_list(list):
  1013. """Finds the GCD of numbers in a list.
  1014. :param list: List of numbers you want to find the GCD of
  1015. E.g. [8, 24, 12]
  1016. :return: GCD of all numbers
  1017. """
  1018. return reduce(gcd, list)
  1019. ###############################################################################
  1020. if __name__ == "__main__":
  1021. import doctest
  1022. doctest.testmod()