temporal_extent.py 48 KB

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