temporal_granularity.py 40 KB

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