temporal_granularity.py 35 KB

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