temporal_granularity.py 35 KB

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