temporal_granularity.py 35 KB

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