temporal_granularity.py 40 KB

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