temporal_granularity.py 38 KB

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