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