temporal_extent.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
  1. """
  2. Temporal extent classes
  3. Usage:
  4. .. code-block:: python
  5. >>> import grass.temporal as tgis
  6. >>> from datetime import datetime
  7. >>> tgis.init()
  8. >>> t = tgis.RasterRelativeTime()
  9. >>> t = tgis.RasterAbsoluteTime()
  10. (C) 2012-2013 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. :authors: Soeren Gebbert
  15. """
  16. from __future__ import print_function
  17. from .base import *
  18. ###############################################################################
  19. class TemporalExtent(SQLDatabaseInterface):
  20. """This is the abstract time base class for relative and absolute time
  21. objects.
  22. It abstract class implements the interface to absolute and relative time.
  23. Absolute time is represented by datetime time stamps,
  24. relative time is represented by a unit an integer value.
  25. This class implements temporal topology relationships computation
  26. after [Allen and Ferguson 1994 Actions and Events in Interval Temporal Logic].
  27. Usage:
  28. .. code-block:: python
  29. >>> init()
  30. >>> A = TemporalExtent(table="raster_absolute_time",
  31. ... ident="soil@PERMANENT", start_time=datetime(2001, 01, 01),
  32. ... end_time=datetime(2005,01,01) )
  33. >>> A.id
  34. 'soil@PERMANENT'
  35. >>> A.start_time
  36. datetime.datetime(2001, 1, 1, 0, 0)
  37. >>> A.end_time
  38. datetime.datetime(2005, 1, 1, 0, 0)
  39. >>> A.print_info()
  40. | Start time:................. 2001-01-01 00:00:00
  41. | End time:................... 2005-01-01 00:00:00
  42. >>> A.print_shell_info()
  43. start_time=2001-01-01 00:00:00
  44. end_time=2005-01-01 00:00:00
  45. >>> # relative time
  46. >>> A = TemporalExtent(table="raster_absolute_time",
  47. ... ident="soil@PERMANENT", start_time=0, end_time=1 )
  48. >>> A.id
  49. 'soil@PERMANENT'
  50. >>> A.start_time
  51. 0
  52. >>> A.end_time
  53. 1
  54. >>> A.print_info()
  55. | Start time:................. 0
  56. | End time:................... 1
  57. >>> A.print_shell_info()
  58. start_time=0
  59. end_time=1
  60. """
  61. def __init__(self, table=None, ident=None, start_time=None, end_time=None):
  62. SQLDatabaseInterface.__init__(self, table, ident)
  63. self.set_id(ident)
  64. self.set_start_time(start_time)
  65. self.set_end_time(end_time)
  66. def intersect(self, extent):
  67. """Intersect this temporal extent with the provided temporal extent and
  68. return a new temporal extent with the new start and end time
  69. :param extent: The temporal extent to intersect with
  70. :return: The new temporal extent with start and end time,
  71. or None in case of no intersection
  72. Usage:
  73. .. code-block:: python
  74. >>> A = TemporalExtent(start_time=5, end_time=6 )
  75. >>> inter = A.intersect(A)
  76. >>> inter.print_info()
  77. | Start time:................. 5
  78. | End time:................... 6
  79. >>> A = TemporalExtent(start_time=5, end_time=6 )
  80. >>> B = TemporalExtent(start_time=5, end_time=7 )
  81. >>> inter = A.intersect(B)
  82. >>> inter.print_info()
  83. | Start time:................. 5
  84. | End time:................... 6
  85. >>> inter = B.intersect(A)
  86. >>> inter.print_info()
  87. | Start time:................. 5
  88. | End time:................... 6
  89. >>> A = TemporalExtent(start_time=3, end_time=6 )
  90. >>> B = TemporalExtent(start_time=5, end_time=7 )
  91. >>> inter = A.intersect(B)
  92. >>> inter.print_info()
  93. | Start time:................. 5
  94. | End time:................... 6
  95. >>> inter = B.intersect(A)
  96. >>> inter.print_info()
  97. | Start time:................. 5
  98. | End time:................... 6
  99. >>> A = TemporalExtent(start_time=3, end_time=8 )
  100. >>> B = TemporalExtent(start_time=5, end_time=6 )
  101. >>> inter = A.intersect(B)
  102. >>> inter.print_info()
  103. | Start time:................. 5
  104. | End time:................... 6
  105. >>> inter = B.intersect(A)
  106. >>> inter.print_info()
  107. | Start time:................. 5
  108. | End time:................... 6
  109. >>> A = TemporalExtent(start_time=5, end_time=8 )
  110. >>> B = TemporalExtent(start_time=3, end_time=6 )
  111. >>> inter = A.intersect(B)
  112. >>> inter.print_info()
  113. | Start time:................. 5
  114. | End time:................... 6
  115. >>> inter = B.intersect(A)
  116. >>> inter.print_info()
  117. | Start time:................. 5
  118. | End time:................... 6
  119. >>> A = TemporalExtent(start_time=5, end_time=None )
  120. >>> B = TemporalExtent(start_time=3, end_time=6 )
  121. >>> inter = A.intersect(B)
  122. >>> inter.print_info()
  123. | Start time:................. 5
  124. | End time:................... None
  125. >>> inter = B.intersect(A)
  126. >>> inter.print_info()
  127. | Start time:................. 5
  128. | End time:................... None
  129. >>> A = TemporalExtent(start_time=5, end_time=8 )
  130. >>> B = TemporalExtent(start_time=3, end_time=4 )
  131. >>> inter = A.intersect(B)
  132. >>> print(inter)
  133. None
  134. >>> A = TemporalExtent(start_time=5, end_time=8 )
  135. >>> B = TemporalExtent(start_time=3, end_time=None )
  136. >>> inter = A.intersect(B)
  137. >>> print(inter)
  138. None
  139. """
  140. relation = self.temporal_relation(extent)
  141. if relation == "after" or relation == "before":
  142. return None
  143. if self.D["end_time"] is None:
  144. return TemporalExtent(start_time=self.D["start_time"])
  145. if extent.D["end_time"] is None:
  146. return TemporalExtent(start_time=extent.D["start_time"])
  147. start = None
  148. end = None
  149. if self.D["start_time"] > extent.D["start_time"]:
  150. start = self.D["start_time"]
  151. else:
  152. start = extent.D["start_time"]
  153. if self.D["end_time"] > extent.D["end_time"]:
  154. end = extent.D["end_time"]
  155. else:
  156. end = self.D["end_time"]
  157. if issubclass(type(self), RelativeTemporalExtent):
  158. return RelativeTemporalExtent(start_time=start, end_time=end,
  159. unit=self.get_unit())
  160. elif issubclass(type(self), AbsoluteTemporalExtent):
  161. return AbsoluteTemporalExtent(start_time=start, end_time=end)
  162. elif issubclass(type(self), TemporalExtent):
  163. return TemporalExtent(start_time=start, end_time=end)
  164. def disjoint_union(self, extent):
  165. """Creates a disjoint union with this temporal extent and the provided one.
  166. Return a new temporal extent with the new start and end time.
  167. :param extent: The temporal extent to create a union with
  168. :return: The new temporal extent with start and end time
  169. Usage:
  170. .. code-block:: python
  171. >>> A = TemporalExtent(start_time=5, end_time=6 )
  172. >>> inter = A.intersect(A)
  173. >>> inter.print_info()
  174. | Start time:................. 5
  175. | End time:................... 6
  176. >>> A = TemporalExtent(start_time=5, end_time=6 )
  177. >>> B = TemporalExtent(start_time=5, end_time=7 )
  178. >>> inter = A.disjoint_union(B)
  179. >>> inter.print_info()
  180. | Start time:................. 5
  181. | End time:................... 7
  182. >>> inter = B.disjoint_union(A)
  183. >>> inter.print_info()
  184. | Start time:................. 5
  185. | End time:................... 7
  186. >>> A = TemporalExtent(start_time=3, end_time=6 )
  187. >>> B = TemporalExtent(start_time=5, end_time=7 )
  188. >>> inter = A.disjoint_union(B)
  189. >>> inter.print_info()
  190. | Start time:................. 3
  191. | End time:................... 7
  192. >>> inter = B.disjoint_union(A)
  193. >>> inter.print_info()
  194. | Start time:................. 3
  195. | End time:................... 7
  196. >>> A = TemporalExtent(start_time=3, end_time=8 )
  197. >>> B = TemporalExtent(start_time=5, end_time=6 )
  198. >>> inter = A.disjoint_union(B)
  199. >>> inter.print_info()
  200. | Start time:................. 3
  201. | End time:................... 8
  202. >>> inter = B.disjoint_union(A)
  203. >>> inter.print_info()
  204. | Start time:................. 3
  205. | End time:................... 8
  206. >>> A = TemporalExtent(start_time=5, end_time=8 )
  207. >>> B = TemporalExtent(start_time=3, end_time=6 )
  208. >>> inter = A.disjoint_union(B)
  209. >>> inter.print_info()
  210. | Start time:................. 3
  211. | End time:................... 8
  212. >>> inter = B.disjoint_union(A)
  213. >>> inter.print_info()
  214. | Start time:................. 3
  215. | End time:................... 8
  216. >>> A = TemporalExtent(start_time=5, end_time=None )
  217. >>> B = TemporalExtent(start_time=3, end_time=6 )
  218. >>> inter = A.disjoint_union(B)
  219. >>> inter.print_info()
  220. | Start time:................. 3
  221. | End time:................... 6
  222. >>> inter = B.disjoint_union(A)
  223. >>> inter.print_info()
  224. | Start time:................. 3
  225. | End time:................... 6
  226. >>> A = TemporalExtent(start_time=5, end_time=8 )
  227. >>> B = TemporalExtent(start_time=3, end_time=4 )
  228. >>> inter = A.disjoint_union(B)
  229. >>> inter.print_info()
  230. | Start time:................. 3
  231. | End time:................... 8
  232. >>> inter = B.disjoint_union(A)
  233. >>> inter.print_info()
  234. | Start time:................. 3
  235. | End time:................... 8
  236. >>> A = TemporalExtent(start_time=5, end_time=8 )
  237. >>> B = TemporalExtent(start_time=3, end_time=None )
  238. >>> inter = A.disjoint_union(B)
  239. >>> inter.print_info()
  240. | Start time:................. 3
  241. | End time:................... 8
  242. >>> inter = B.disjoint_union(A)
  243. >>> inter.print_info()
  244. | Start time:................. 3
  245. | End time:................... 8
  246. >>> A = TemporalExtent(start_time=5, end_time=None )
  247. >>> B = TemporalExtent(start_time=3, end_time=8 )
  248. >>> inter = A.disjoint_union(B)
  249. >>> inter.print_info()
  250. | Start time:................. 3
  251. | End time:................... 8
  252. >>> inter = B.disjoint_union(A)
  253. >>> inter.print_info()
  254. | Start time:................. 3
  255. | End time:................... 8
  256. >>> A = TemporalExtent(start_time=5, end_time=None )
  257. >>> B = TemporalExtent(start_time=3, end_time=None )
  258. >>> inter = A.disjoint_union(B)
  259. >>> inter.print_info()
  260. | Start time:................. 3
  261. | End time:................... 5
  262. >>> inter = B.disjoint_union(A)
  263. >>> inter.print_info()
  264. | Start time:................. 3
  265. | End time:................... 5
  266. >>> A = RelativeTemporalExtent(start_time=5, end_time=None, unit="years" )
  267. >>> B = RelativeTemporalExtent(start_time=3, end_time=None, unit="years" )
  268. >>> inter = A.disjoint_union(B)
  269. >>> inter.print_info()
  270. +-------------------- Relative time -----------------------------------------+
  271. | Start time:................. 3
  272. | End time:................... 5
  273. | Relative time unit:......... years
  274. >>> inter = B.disjoint_union(A)
  275. >>> inter.print_info()
  276. +-------------------- Relative time -----------------------------------------+
  277. | Start time:................. 3
  278. | End time:................... 5
  279. | Relative time unit:......... years
  280. >>> from datetime import datetime as dt
  281. >>> A = AbsoluteTemporalExtent(start_time=dt(2001,1,10), end_time=dt(2003,1,1))
  282. >>> B = AbsoluteTemporalExtent(start_time=dt(2005,1,10), end_time=dt(2008,1,1))
  283. >>> inter = A.disjoint_union(B)
  284. >>> inter.print_info()
  285. +-------------------- Absolute time -----------------------------------------+
  286. | Start time:................. 2001-01-10 00:00:00
  287. | End time:................... 2008-01-01 00:00:00
  288. >>> inter = B.disjoint_union(A)
  289. >>> inter.print_info()
  290. +-------------------- Absolute time -----------------------------------------+
  291. | Start time:................. 2001-01-10 00:00:00
  292. | End time:................... 2008-01-01 00:00:00
  293. """
  294. start = None
  295. end = None
  296. if self.D["start_time"] < extent.D["start_time"]:
  297. start = self.D["start_time"]
  298. else:
  299. start = extent.D["start_time"]
  300. # End time handling
  301. if self.D["end_time"] is None and extent.D["end_time"] is None:
  302. if self.D["start_time"] > extent.D["start_time"]:
  303. end = self.D["start_time"]
  304. else:
  305. end = extent.D["start_time"]
  306. elif self.D["end_time"] is None:
  307. if self.D["start_time"] > extent.D["end_time"]:
  308. end = self.D["start_time"]
  309. else:
  310. end = extent.D["end_time"]
  311. elif extent.D["end_time"] is None:
  312. if self.D["end_time"] > extent.D["start_time"]:
  313. end = self.D["end_time"]
  314. else:
  315. end = extent.D["start_time"]
  316. elif self.D["end_time"] < extent.D["end_time"]:
  317. end = extent.D["end_time"]
  318. else:
  319. end = self.D["end_time"]
  320. if issubclass(type(self), RelativeTemporalExtent):
  321. return RelativeTemporalExtent(start_time=start, end_time=end,
  322. unit=self.get_unit())
  323. elif issubclass(type(self), AbsoluteTemporalExtent):
  324. return AbsoluteTemporalExtent(start_time=start, end_time=end)
  325. elif issubclass(type(self), TemporalExtent):
  326. return TemporalExtent(start_time=start, end_time=end)
  327. def union(self, extent):
  328. """Creates a union with this temporal extent and the provided one.
  329. Return a new temporal extent with the new start and end time.
  330. :param extent: The temporal extent to create a union with
  331. :return: The new temporal extent with start and end time,
  332. or None in case the temporal extents are unrelated
  333. (before or after)
  334. .. code-block:: python
  335. >>> A = TemporalExtent(start_time=5, end_time=8 )
  336. >>> B = TemporalExtent(start_time=3, end_time=4 )
  337. >>> inter = A.intersect(B)
  338. >>> print(inter)
  339. None
  340. >>> A = TemporalExtent(start_time=5, end_time=8 )
  341. >>> B = TemporalExtent(start_time=3, end_time=None )
  342. >>> inter = A.intersect(B)
  343. >>> print(inter)
  344. None
  345. """
  346. relation = self.temporal_relation(extent)
  347. if relation == "after" or relation == "before":
  348. return None
  349. return self.disjoint_union(extent)
  350. def starts(self, extent):
  351. """Return True if this temporal extent (A) starts at the start of the
  352. provided temporal extent (B) and finishes within it
  353. ::
  354. A |-----|
  355. B |---------|
  356. :param extent: The temporal extent object with which this extent
  357. starts
  358. Usage:
  359. .. code-block:: python
  360. >>> A = TemporalExtent(start_time=5, end_time=6 )
  361. >>> B = TemporalExtent(start_time=5, end_time=7 )
  362. >>> A.starts(B)
  363. True
  364. >>> B.starts(A)
  365. False
  366. """
  367. if self.D["end_time"] is None or extent.D["end_time"] is None:
  368. return False
  369. if self.D["start_time"] == extent.D["start_time"] and \
  370. self.D["end_time"] < extent.D["end_time"]:
  371. return True
  372. else:
  373. return False
  374. def started(self, extent):
  375. """Return True if this temporal extent (A) started at the start of the
  376. provided temporal extent (B) and finishes after it
  377. ::
  378. A |---------|
  379. B |-----|
  380. :param extent: The temporal extent object with which this extent
  381. started
  382. Usage:
  383. .. code-block:: python
  384. >>> A = TemporalExtent(start_time=5, end_time=7 )
  385. >>> B = TemporalExtent(start_time=5, end_time=6 )
  386. >>> A.started(B)
  387. True
  388. >>> B.started(A)
  389. False
  390. """
  391. if self.D["end_time"] is None or extent.D["end_time"] is None:
  392. return False
  393. if self.D["start_time"] == extent.D["start_time"] and \
  394. self.D["end_time"] > extent.D["end_time"]:
  395. return True
  396. else:
  397. return False
  398. def finishes(self, extent):
  399. """Return True if this temporal extent (A) starts after the start of
  400. the provided temporal extent (B) and finishes with it
  401. ::
  402. A |-----|
  403. B |---------|
  404. :param extent: The temporal extent object with which this extent
  405. finishes
  406. Usage:
  407. .. code-block:: python
  408. >>> A = TemporalExtent(start_time=6, end_time=7 )
  409. >>> B = TemporalExtent(start_time=5, end_time=7 )
  410. >>> A.finishes(B)
  411. True
  412. >>> B.finishes(A)
  413. False
  414. """
  415. if self.D["end_time"] is None or extent.D["end_time"] is None:
  416. return False
  417. if self.D["end_time"] == extent.D["end_time"] and \
  418. self.D["start_time"] > extent.D["start_time"]:
  419. return True
  420. else:
  421. return False
  422. def finished(self, extent):
  423. """Return True if this temporal extent (A) starts before the start of
  424. the provided temporal extent (B) and finishes with it
  425. ::
  426. A |---------|
  427. B |-----|
  428. :param extent: The temporal extent object with which this extent
  429. finishes
  430. Usage:
  431. .. code-block:: python
  432. >>> A = TemporalExtent(start_time=5, end_time=7 )
  433. >>> B = TemporalExtent(start_time=6, end_time=7 )
  434. >>> A.finished(B)
  435. True
  436. >>> B.finished(A)
  437. False
  438. """
  439. if self.D["end_time"] is None or extent.D["end_time"] is None:
  440. return False
  441. if self.D["end_time"] == extent.D["end_time"] and \
  442. self.D["start_time"] < extent.D["start_time"]:
  443. return True
  444. else:
  445. return False
  446. def after(self, extent):
  447. """Return True if this temporal extent (A) is located after the
  448. provided temporal extent (B)
  449. ::
  450. A |---------|
  451. B |---------|
  452. :param extent: The temporal extent object that is located before
  453. this extent
  454. Usage:
  455. .. code-block:: python
  456. >>> A = TemporalExtent(start_time=8, end_time=9 )
  457. >>> B = TemporalExtent(start_time=6, end_time=7 )
  458. >>> A.after(B)
  459. True
  460. >>> B.after(A)
  461. False
  462. """
  463. if extent.D["end_time"] is None:
  464. if self.D["start_time"] > extent.D["start_time"]:
  465. return True
  466. else:
  467. return False
  468. if self.D["start_time"] > extent.D["end_time"]:
  469. return True
  470. else:
  471. return False
  472. def before(self, extent):
  473. """Return True if this temporal extent (A) is located before the
  474. provided temporal extent (B)
  475. ::
  476. A |---------|
  477. B |---------|
  478. :param extent: The temporal extent object that is located after
  479. this extent
  480. Usage:
  481. .. code-block:: python
  482. >>> A = TemporalExtent(start_time=6, end_time=7 )
  483. >>> B = TemporalExtent(start_time=8, end_time=9 )
  484. >>> A.before(B)
  485. True
  486. >>> B.before(A)
  487. False
  488. """
  489. if self.D["end_time"] is None:
  490. if self.D["start_time"] < extent.D["start_time"]:
  491. return True
  492. else:
  493. return False
  494. if self.D["end_time"] < extent.D["start_time"]:
  495. return True
  496. else:
  497. return False
  498. def adjacent(self, extent):
  499. """Return True if this temporal extent (A) is a meeting neighbor the
  500. provided temporal extent (B)
  501. ::
  502. A |---------|
  503. B |---------|
  504. A |---------|
  505. B |---------|
  506. :param extent: The temporal extent object that is a meeting neighbor
  507. of this extent
  508. Usage:
  509. .. code-block:: python
  510. >>> A = TemporalExtent(start_time=5, end_time=7 )
  511. >>> B = TemporalExtent(start_time=7, end_time=9 )
  512. >>> A.adjacent(B)
  513. True
  514. >>> B.adjacent(A)
  515. True
  516. >>> A = TemporalExtent(start_time=5, end_time=7 )
  517. >>> B = TemporalExtent(start_time=3, end_time=5 )
  518. >>> A.adjacent(B)
  519. True
  520. >>> B.adjacent(A)
  521. True
  522. """
  523. if self.D["end_time"] is None and extent.D["end_time"] is None:
  524. return False
  525. if (self.D["start_time"] == extent.D["end_time"]) or \
  526. (self.D["end_time"] == extent.D["start_time"]):
  527. return True
  528. else:
  529. return False
  530. def follows(self, extent):
  531. """Return True if this temporal extent (A) follows the
  532. provided temporal extent (B)
  533. ::
  534. A |---------|
  535. B |---------|
  536. :param extent: The temporal extent object that is the predecessor
  537. of this extent
  538. Usage:
  539. .. code-block:: python
  540. >>> A = TemporalExtent(start_time=5, end_time=7 )
  541. >>> B = TemporalExtent(start_time=3, end_time=5 )
  542. >>> A.follows(B)
  543. True
  544. >>> B.follows(A)
  545. False
  546. """
  547. if extent.D["end_time"] is None:
  548. return False
  549. if self.D["start_time"] == extent.D["end_time"]:
  550. return True
  551. else:
  552. return False
  553. def precedes(self, extent):
  554. """Return True if this temporal extent (A) precedes the provided
  555. temporal extent (B)
  556. ::
  557. A |---------|
  558. B |---------|
  559. :param extent: The temporal extent object that is the successor
  560. of this extent
  561. Usage:
  562. .. code-block:: python
  563. >>> A = TemporalExtent(start_time=5, end_time=7 )
  564. >>> B = TemporalExtent(start_time=7, end_time=9 )
  565. >>> A.precedes(B)
  566. True
  567. >>> B.precedes(A)
  568. False
  569. """
  570. if self.D["end_time"] is None:
  571. return False
  572. if self.D["end_time"] == extent.D["start_time"]:
  573. return True
  574. else:
  575. return False
  576. def during(self, extent):
  577. """Return True if this temporal extent (A) is located during the provided
  578. temporal extent (B)
  579. ::
  580. A |-------|
  581. B |---------|
  582. :param extent: The temporal extent object that contains this extent
  583. Usage:
  584. .. code-block:: python
  585. >>> A = TemporalExtent(start_time=5, end_time=7 )
  586. >>> B = TemporalExtent(start_time=4, end_time=9 )
  587. >>> A.during(B)
  588. True
  589. >>> B.during(A)
  590. False
  591. """
  592. # Check single point of time in interval
  593. if extent.D["end_time"] is None:
  594. return False
  595. # Check single point of time in interval
  596. if self.D["end_time"] is None:
  597. if self.D["start_time"] >= extent.D["start_time"] and \
  598. self.D["start_time"] < extent.D["end_time"]:
  599. return True
  600. else:
  601. return False
  602. if self.D["start_time"] > extent.D["start_time"] and \
  603. self.D["end_time"] < extent.D["end_time"]:
  604. return True
  605. else:
  606. return False
  607. def contains(self, extent):
  608. """Return True if this temporal extent (A) contains the provided
  609. temporal extent (B)
  610. ::
  611. A |---------|
  612. B |-------|
  613. :param extent: The temporal extent object that is located
  614. during this extent
  615. Usage:
  616. .. code-block:: python
  617. >>> A = TemporalExtent(start_time=4, end_time=9 )
  618. >>> B = TemporalExtent(start_time=5, end_time=8 )
  619. >>> A.contains(B)
  620. True
  621. >>> B.contains(A)
  622. False
  623. """
  624. # Check single point of time in interval
  625. if self.D["end_time"] is None:
  626. return False
  627. # Check single point of time in interval
  628. if extent.D["end_time"] is None:
  629. if self.D["start_time"] <= extent.D["start_time"] and \
  630. self.D["end_time"] > extent.D["start_time"]:
  631. return True
  632. else:
  633. return False
  634. if self.D["start_time"] < extent.D["start_time"] and \
  635. self.D["end_time"] > extent.D["end_time"]:
  636. return True
  637. else:
  638. return False
  639. def equal(self, extent):
  640. """Return True if this temporal extent (A) is equal to the provided
  641. temporal extent (B)
  642. ::
  643. A |---------|
  644. B |---------|
  645. :param extent: The temporal extent object that is equal
  646. during this extent
  647. Usage:
  648. .. code-block:: python
  649. >>> A = TemporalExtent(start_time=5, end_time=6 )
  650. >>> B = TemporalExtent(start_time=5, end_time=6 )
  651. >>> A.equal(B)
  652. True
  653. >>> B.equal(A)
  654. True
  655. """
  656. if self.D["end_time"] is None and extent.D["end_time"] is None:
  657. if self.D["start_time"] == extent.D["start_time"]:
  658. return True
  659. else:
  660. return False
  661. if self.D["end_time"] is None or extent.D["end_time"] is None:
  662. return False
  663. if self.D["start_time"] == extent.D["start_time"] and \
  664. self.D["end_time"] == extent.D["end_time"]:
  665. return True
  666. else:
  667. return False
  668. def overlaps(self, extent):
  669. """Return True if this temporal extent (A) overlapped the provided
  670. temporal extent (B)
  671. ::
  672. A |---------|
  673. B |---------|
  674. :param extent: The temporal extent object that is overlaps
  675. this extent
  676. Usage:
  677. .. code-block:: python
  678. >>> A = TemporalExtent(start_time=5, end_time=7 )
  679. >>> B = TemporalExtent(start_time=6, end_time=8 )
  680. >>> A.overlaps(B)
  681. True
  682. >>> B.overlaps(A)
  683. False
  684. >>> A = TemporalExtent(start_time=5, end_time=6 )
  685. >>> B = TemporalExtent(start_time=6, end_time=8 )
  686. >>> A.overlaps(B)
  687. False
  688. >>> B.overlaps(A)
  689. False
  690. """
  691. if self.D["end_time"] is None or extent.D["end_time"] is None:
  692. return False
  693. if self.D["start_time"] < extent.D["start_time"] and \
  694. self.D["end_time"] < extent.D["end_time"] and \
  695. self.D["end_time"] > extent.D["start_time"]:
  696. return True
  697. else:
  698. return False
  699. def overlapped(self, extent):
  700. """Return True if this temporal extent (A) overlapps the provided
  701. temporal extent (B)
  702. ::
  703. A |---------|
  704. B |---------|
  705. :param extent: The temporal extent object that is overlapped
  706. this extent
  707. Usage:
  708. .. code-block:: python
  709. >>> A = TemporalExtent(start_time=6, end_time=8 )
  710. >>> B = TemporalExtent(start_time=5, end_time=7 )
  711. >>> A.overlapped(B)
  712. True
  713. >>> B.overlapped(A)
  714. False
  715. >>> A = TemporalExtent(start_time=6, end_time=8 )
  716. >>> B = TemporalExtent(start_time=5, end_time=6 )
  717. >>> A.overlapped(B)
  718. False
  719. >>> B.overlapped(A)
  720. False
  721. """
  722. if self.D["end_time"] is None or extent.D["end_time"] is None:
  723. return False
  724. if self.D["start_time"] > extent.D["start_time"] and \
  725. self.D["end_time"] > extent.D["end_time"] and \
  726. self.D["start_time"] < extent.D["end_time"]:
  727. return True
  728. else:
  729. return False
  730. def temporal_relation(self, extent):
  731. """Returns the temporal relation between temporal objects
  732. Temporal relationships are implemented after
  733. [Allen and Ferguson 1994 Actions and Events in Interval Temporal Logic]
  734. The following temporal relationships are supported:
  735. - equal
  736. - during
  737. - contains
  738. - overlaps
  739. - overlapped
  740. - after
  741. - before
  742. - starts
  743. - finishes
  744. - started
  745. - finished
  746. - follows
  747. - precedes
  748. :param extent: The temporal extent
  749. :return: The name of the temporal relation or None if no relation
  750. found
  751. """
  752. # First check for correct time
  753. if "start_time" not in self.D:
  754. return None
  755. if "end_time" not in self.D:
  756. return None
  757. if "start_time" not in extent.D:
  758. return None
  759. if "end_time" not in extent.D:
  760. return None
  761. # Return None if the start_time is undefined
  762. if self.D["start_time"] is None or extent.D["start_time"] is None:
  763. return None
  764. if self.equal(extent):
  765. return "equal"
  766. if self.during(extent):
  767. return "during"
  768. if self.contains(extent):
  769. return "contains"
  770. if self.overlaps(extent):
  771. return "overlaps"
  772. if self.overlapped(extent):
  773. return "overlapped"
  774. if self.after(extent):
  775. return "after"
  776. if self.before(extent):
  777. return "before"
  778. if self.starts(extent):
  779. return "starts"
  780. if self.finishes(extent):
  781. return "finishes"
  782. if self.started(extent):
  783. return "started"
  784. if self.finished(extent):
  785. return "finished"
  786. if self.follows(extent):
  787. return "follows"
  788. if self.precedes(extent):
  789. return "precedes"
  790. return None
  791. def set_id(self, ident):
  792. """Convenient method to set the unique identifier (primary key)"""
  793. self.ident = ident
  794. self.D["id"] = ident
  795. def set_start_time(self, start_time):
  796. """Set the valid start time of the extent"""
  797. self.D["start_time"] = start_time
  798. def set_end_time(self, end_time):
  799. """Set the valid end time of the extent"""
  800. self.D["end_time"] = end_time
  801. def get_id(self):
  802. """Convenient method to get the unique identifier (primary key)
  803. :return: None if not found
  804. """
  805. if "id" in self.D:
  806. return self.D["id"]
  807. else:
  808. return None
  809. def get_start_time(self):
  810. """Get the valid start time of the extent
  811. :return: None if not found"""
  812. if "start_time" in self.D:
  813. return self.D["start_time"]
  814. else:
  815. return None
  816. def get_end_time(self):
  817. """Get the valid end time of the extent
  818. :return: None if not found"""
  819. if "end_time" in self.D:
  820. return self.D["end_time"]
  821. else:
  822. return None
  823. # Set the properties
  824. id = property(fget=get_id, fset=set_id)
  825. start_time = property(fget=get_start_time, fset=set_start_time)
  826. end_time = property(fget=get_end_time, fset=set_end_time)
  827. def print_info(self):
  828. """Print information about this class in human readable style"""
  829. # 0123456789012345678901234567890
  830. print(" | Start time:................. " + str(self.get_start_time()))
  831. print(" | End time:................... " + str(self.get_end_time()))
  832. def print_shell_info(self):
  833. """Print information about this class in shell style"""
  834. print("start_time=" + str(self.get_start_time()))
  835. print("end_time=" + str(self.get_end_time()))
  836. ###############################################################################
  837. class AbsoluteTemporalExtent(TemporalExtent):
  838. """This is the absolute time class for all maps and spacetime datasets
  839. start_time and end_time must be of type datetime
  840. """
  841. def __init__(self, table=None, ident=None, start_time=None, end_time=None):
  842. TemporalExtent.__init__(
  843. self, table, ident, start_time, end_time)
  844. def print_info(self):
  845. """Print information about this class in human readable style"""
  846. # 0123456789012345678901234567890
  847. print(" +-------------------- Absolute time -----------------------------------------+")
  848. TemporalExtent.print_info(self)
  849. def print_shell_info(self):
  850. """Print information about this class in shell style"""
  851. TemporalExtent.print_shell_info(self)
  852. ###############################################################################
  853. class RasterAbsoluteTime(AbsoluteTemporalExtent):
  854. def __init__(self, ident=None, start_time=None, end_time=None):
  855. AbsoluteTemporalExtent.__init__(self, "raster_absolute_time",
  856. ident, start_time, end_time)
  857. class Raster3DAbsoluteTime(AbsoluteTemporalExtent):
  858. def __init__(self, ident=None, start_time=None, end_time=None):
  859. AbsoluteTemporalExtent.__init__(self, "raster3d_absolute_time",
  860. ident, start_time, end_time)
  861. class VectorAbsoluteTime(AbsoluteTemporalExtent):
  862. def __init__(self, ident=None, start_time=None, end_time=None):
  863. AbsoluteTemporalExtent.__init__(self, "vector_absolute_time",
  864. ident, start_time, end_time)
  865. ###############################################################################
  866. class STDSAbsoluteTime(AbsoluteTemporalExtent):
  867. """This class implements the absolute time extent for space time dataset
  868. In addition to the existing functionality the granularity and the
  869. map_time are added.
  870. Usage:
  871. .. code-block:: python
  872. >>> init()
  873. >>> A = STDSAbsoluteTime(table="strds_absolute_time",
  874. ... ident="strds@PERMANENT", start_time=datetime(2001, 01, 01),
  875. ... end_time=datetime(2005,01,01), granularity="1 days",
  876. ... map_time="interval")
  877. >>> A.id
  878. 'strds@PERMANENT'
  879. >>> A.start_time
  880. datetime.datetime(2001, 1, 1, 0, 0)
  881. >>> A.end_time
  882. datetime.datetime(2005, 1, 1, 0, 0)
  883. >>> A.granularity
  884. '1 days'
  885. >>> A.map_time
  886. 'interval'
  887. >>> A.print_info()
  888. +-------------------- Absolute time -----------------------------------------+
  889. | Start time:................. 2001-01-01 00:00:00
  890. | End time:................... 2005-01-01 00:00:00
  891. | Granularity:................ 1 days
  892. | Temporal type of maps:...... interval
  893. >>> A.print_shell_info()
  894. start_time=2001-01-01 00:00:00
  895. end_time=2005-01-01 00:00:00
  896. granularity=1 days
  897. map_time=interval
  898. """
  899. def __init__(self, table=None, ident=None, start_time=None, end_time=None,
  900. granularity=None, map_time=None):
  901. AbsoluteTemporalExtent.__init__(
  902. self, table, ident, start_time, end_time)
  903. self.set_granularity(granularity)
  904. self.set_map_time(map_time)
  905. def set_granularity(self, granularity):
  906. """Set the granularity of the space time dataset"""
  907. self.D["granularity"] = granularity
  908. def set_map_time(self, map_time):
  909. """Set the type of the map time
  910. Registered maps may have different types of time:
  911. - Single point of time "point"
  912. - Time intervals "interval"
  913. - Single point and interval time "mixed"
  914. This variable will be set automatically when maps are registered.
  915. """
  916. self.D["map_time"] = map_time
  917. def get_granularity(self):
  918. """Get the granularity of the space time dataset
  919. :return: None if not found"""
  920. if "granularity" in self.D:
  921. return self.D["granularity"]
  922. else:
  923. return None
  924. def get_map_time(self):
  925. """Get the type of the map time
  926. Registered maps may have different types of time:
  927. - Single point of time "point"
  928. - Time intervals "interval"
  929. - Single point and interval time "mixed"
  930. This variable will be set automatically when maps are registered.
  931. """
  932. if "map_time" in self.D:
  933. return self.D["map_time"]
  934. else:
  935. return None
  936. # Properties
  937. granularity = property(fget=get_granularity, fset=set_granularity)
  938. map_time = property(fget=get_map_time, fset=set_map_time)
  939. def print_info(self):
  940. """Print information about this class in human readable style"""
  941. AbsoluteTemporalExtent.print_info(self)
  942. # 0123456789012345678901234567890
  943. print(" | Granularity:................ " + str(self.get_granularity()))
  944. print(" | Temporal type of maps:...... " + str(self.get_map_time()))
  945. def print_shell_info(self):
  946. """Print information about this class in shell style"""
  947. AbsoluteTemporalExtent.print_shell_info(self)
  948. print("granularity=" + str(self.get_granularity()))
  949. print("map_time=" + str(self.get_map_time()))
  950. ###############################################################################
  951. class STRDSAbsoluteTime(STDSAbsoluteTime):
  952. def __init__(self, ident=None, start_time=None, end_time=None,
  953. granularity=None):
  954. STDSAbsoluteTime.__init__(self, "strds_absolute_time",
  955. ident, start_time, end_time, granularity)
  956. class STR3DSAbsoluteTime(STDSAbsoluteTime):
  957. def __init__(self, ident=None, start_time=None, end_time=None,
  958. granularity=None):
  959. STDSAbsoluteTime.__init__(self, "str3ds_absolute_time",
  960. ident, start_time, end_time, granularity)
  961. class STVDSAbsoluteTime(STDSAbsoluteTime):
  962. def __init__(self, ident=None, start_time=None, end_time=None,
  963. granularity=None):
  964. STDSAbsoluteTime.__init__(self, "stvds_absolute_time",
  965. ident, start_time, end_time, granularity)
  966. ###############################################################################
  967. class RelativeTemporalExtent(TemporalExtent):
  968. """This is the relative time class for all maps and space time datasets
  969. start_time and end_time must be of type integer
  970. Usage:
  971. .. code-block:: python
  972. >>> init()
  973. >>> A = RelativeTemporalExtent(table="raster_relative_time",
  974. ... ident="soil@PERMANENT", start_time=0, end_time=1, unit="years")
  975. >>> A.id
  976. 'soil@PERMANENT'
  977. >>> A.start_time
  978. 0
  979. >>> A.end_time
  980. 1
  981. >>> A.unit
  982. 'years'
  983. >>> A.print_info()
  984. +-------------------- Relative time -----------------------------------------+
  985. | Start time:................. 0
  986. | End time:................... 1
  987. | Relative time unit:......... years
  988. >>> A.print_shell_info()
  989. start_time=0
  990. end_time=1
  991. unit=years
  992. """
  993. def __init__(self, table=None, ident=None, start_time=None, end_time=None,
  994. unit=None):
  995. TemporalExtent.__init__(
  996. self, table, ident, start_time, end_time)
  997. self.set_unit(unit)
  998. def set_unit(self, unit):
  999. """Set the unit of the relative time. Valid units are:
  1000. - years
  1001. - months
  1002. - days
  1003. - hours
  1004. - minutes
  1005. - seconds
  1006. """
  1007. self.D["unit"] = unit
  1008. def get_unit(self):
  1009. """Get the unit of the relative time
  1010. :return: None if not found"""
  1011. if "unit" in self.D:
  1012. return self.D["unit"]
  1013. else:
  1014. return None
  1015. def temporal_relation(self, map):
  1016. """Returns the temporal relation between temporal objects
  1017. Temporal relationships are implemented after
  1018. [Allen and Ferguson 1994 Actions and Events in Interval Temporal Logic]
  1019. """
  1020. # Check units for relative time
  1021. if "unit" not in self.D:
  1022. return None
  1023. if "unit" not in map.D:
  1024. return None
  1025. # Units must be equal
  1026. if self.D["unit"] != map.D["unit"]:
  1027. return None
  1028. return TemporalExtent.temporal_relation(self, map)
  1029. # Properties
  1030. unit = property(fget=get_unit, fset=set_unit)
  1031. def print_info(self):
  1032. """Print information about this class in human readable style"""
  1033. # 0123456789012345678901234567890
  1034. print(" +-------------------- Relative time -----------------------------------------+")
  1035. TemporalExtent.print_info(self)
  1036. print(" | Relative time unit:......... " + str(self.get_unit()))
  1037. def print_shell_info(self):
  1038. """Print information about this class in shell style"""
  1039. TemporalExtent.print_shell_info(self)
  1040. print("unit=" + str(self.get_unit()))
  1041. ###############################################################################
  1042. class RasterRelativeTime(RelativeTemporalExtent):
  1043. def __init__(self, ident=None, start_time=None, end_time=None,
  1044. unit=None):
  1045. RelativeTemporalExtent.__init__(self, "raster_relative_time", ident,
  1046. start_time, end_time, unit)
  1047. class Raster3DRelativeTime(RelativeTemporalExtent):
  1048. def __init__(self, ident=None, start_time=None, end_time=None,
  1049. unit=None):
  1050. RelativeTemporalExtent.__init__(self, "raster3d_relative_time", ident,
  1051. start_time, end_time, unit)
  1052. class VectorRelativeTime(RelativeTemporalExtent):
  1053. def __init__(self, ident=None, start_time=None, end_time=None,
  1054. unit=None):
  1055. RelativeTemporalExtent.__init__(
  1056. self, "vector_relative_time", ident, start_time, end_time, unit)
  1057. ###############################################################################
  1058. class STDSRelativeTime(RelativeTemporalExtent):
  1059. """This is the relative time class for all maps and space time datasets
  1060. start_time and end_time must be of type integer
  1061. Usage:
  1062. .. code-block:: python
  1063. >>> init()
  1064. >>> A = STDSRelativeTime(table="strds_relative_time",
  1065. ... ident="strds@PERMANENT", start_time=0, end_time=1, unit="years",
  1066. ... granularity=5, map_time="interval")
  1067. >>> A.id
  1068. 'strds@PERMANENT'
  1069. >>> A.start_time
  1070. 0
  1071. >>> A.end_time
  1072. 1
  1073. >>> A.unit
  1074. 'years'
  1075. >>> A.granularity
  1076. 5
  1077. >>> A.map_time
  1078. 'interval'
  1079. >>> A.print_info()
  1080. +-------------------- Relative time -----------------------------------------+
  1081. | Start time:................. 0
  1082. | End time:................... 1
  1083. | Relative time unit:......... years
  1084. | Granularity:................ 5
  1085. | Temporal type of maps:...... interval
  1086. >>> A.print_shell_info()
  1087. start_time=0
  1088. end_time=1
  1089. unit=years
  1090. granularity=5
  1091. map_time=interval
  1092. """
  1093. def __init__(self, table=None, ident=None, start_time=None, end_time=None,
  1094. unit=None, granularity=None, map_time=None):
  1095. RelativeTemporalExtent.__init__(
  1096. self, table, ident, start_time, end_time, unit)
  1097. self.set_granularity(granularity)
  1098. self.set_map_time(map_time)
  1099. def set_granularity(self, granularity):
  1100. """Set the granularity of the space time dataset"""
  1101. self.D["granularity"] = granularity
  1102. def set_map_time(self, map_time):
  1103. """Set the type of the map time
  1104. Registered maps may have different types of time:
  1105. - Single point of time "point"
  1106. - Time intervals "interval"
  1107. - Single point and interval time "mixed"
  1108. This variable will be set automatically when maps are registered.
  1109. """
  1110. self.D["map_time"] = map_time
  1111. def get_granularity(self):
  1112. """Get the granularity of the space time dataset
  1113. :return: None if not found"""
  1114. if "granularity" in self.D:
  1115. return self.D["granularity"]
  1116. else:
  1117. return None
  1118. def get_map_time(self):
  1119. """Get the type of the map time
  1120. Registered maps may have different types of time:
  1121. - Single point of time "point"
  1122. - Time intervals "interval"
  1123. - Single point and interval time "mixed"
  1124. This variable will be set automatically when maps are registered.
  1125. """
  1126. if "map_time" in self.D:
  1127. return self.D["map_time"]
  1128. else:
  1129. return None
  1130. # Properties
  1131. granularity = property(fget=get_granularity, fset=set_granularity)
  1132. map_time = property(fget=get_map_time, fset=set_map_time)
  1133. def print_info(self):
  1134. """Print information about this class in human readable style"""
  1135. RelativeTemporalExtent.print_info(self)
  1136. # 0123456789012345678901234567890
  1137. print(" | Granularity:................ " + str(self.get_granularity()))
  1138. print(" | Temporal type of maps:...... " + str(self.get_map_time()))
  1139. def print_shell_info(self):
  1140. """Print information about this class in shell style"""
  1141. RelativeTemporalExtent.print_shell_info(self)
  1142. print("granularity=" + str(self.get_granularity()))
  1143. print("map_time=" + str(self.get_map_time()))
  1144. ###############################################################################
  1145. class STRDSRelativeTime(STDSRelativeTime):
  1146. def __init__(self, ident=None, start_time=None, end_time=None,
  1147. unit=None, granularity=None, map_time=None):
  1148. STDSRelativeTime.__init__(self, "strds_relative_time", ident,
  1149. start_time, end_time, unit, granularity,
  1150. map_time)
  1151. class STR3DSRelativeTime(STDSRelativeTime):
  1152. def __init__(self, ident=None, start_time=None, end_time=None,
  1153. unit=None, granularity=None, map_time=None):
  1154. STDSRelativeTime.__init__(self, "str3ds_relative_time", ident,
  1155. start_time, end_time, unit, granularity,
  1156. map_time)
  1157. class STVDSRelativeTime(STDSRelativeTime):
  1158. def __init__(self, ident=None, start_time=None, end_time=None,
  1159. unit=None, granularity=None, map_time=None):
  1160. STDSRelativeTime.__init__(self, "stvds_relative_time", ident,
  1161. start_time, end_time, unit, granularity,
  1162. map_time)
  1163. ###############################################################################
  1164. if __name__ == "__main__":
  1165. import doctest
  1166. doctest.testmod()