1""" 2Map layer and space time dataset classes 3 4(C) 2012-2013 by the GRASS Development Team 5This program is free software under the GNU General Public 6License (>=v2). Read the file COPYING that comes with GRASS 7for details. 8 9:authors: Soeren Gebbert 10""" 11import getpass 12from datetime import datetime 13from .core import get_current_mapset 14from .abstract_map_dataset import AbstractMapDataset 15from .abstract_space_time_dataset import AbstractSpaceTimeDataset 16from .base import Raster3DBase, RasterBase, VectorBase, STR3DSBase, STVDSBase, STRDSBase,\ 17 VectorSTDSRegister, Raster3DSTDSRegister, RasterSTDSRegister 18from .metadata import Raster3DMetadata, RasterMetadata, VectorMetadata, STRDSMetadata,\ 19 STR3DSMetadata, STVDSMetadata 20from .spatial_extent import RasterSpatialExtent, Raster3DSpatialExtent, VectorSpatialExtent,\ 21 STRDSSpatialExtent, STR3DSSpatialExtent, STVDSSpatialExtent 22from .temporal_extent import RasterAbsoluteTime, RasterRelativeTime, Raster3DAbsoluteTime, \ 23 Raster3DRelativeTime, VectorAbsoluteTime, VectorRelativeTime, STRDSAbsoluteTime,\ 24 STRDSRelativeTime, STR3DSAbsoluteTime, STR3DSRelativeTime, STVDSAbsoluteTime, STVDSRelativeTime 25import grass.script.array as garray 26from .core import init 27from datetime import datetime 28 29############################################################################### 30 31 32class RasterDataset(AbstractMapDataset): 33 """Raster dataset class 34 35 This class provides functions to select, update, insert or delete raster 36 map information and valid time stamps into the SQL temporal database. 37 38 Usage: 39 40 .. code-block:: python 41 42 >>> import grass.script as grass 43 >>> import grass.temporal as tgis 44 >>> init() 45 >>> grass.use_temp_region() 46 >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0, 47 ... t=1.0, b=0.0, res=10.0) 48 0 49 >>> grass.run_command("r.mapcalc", overwrite=True, quiet=True, 50 ... expression="strds_map_test_case = 1") 51 0 52 >>> grass.run_command("r.timestamp", map="strds_map_test_case", 53 ... date="15 jan 1999", quiet=True) 54 0 55 >>> mapset = tgis.get_current_mapset() 56 >>> name = "strds_map_test_case" 57 >>> identifier = "%s@%s" % (name, mapset) 58 >>> rmap = RasterDataset(identifier) 59 >>> rmap.map_exists() 60 True 61 >>> rmap.read_timestamp_from_grass() 62 True 63 >>> rmap.get_temporal_extent_as_tuple() 64 (datetime.datetime(1999, 1, 15, 0, 0), None) 65 >>> rmap.load() 66 True 67 >>> rmap.spatial_extent.print_info() 68 +-------------------- Spatial extent ----------------------------------------+ 69 | North:...................... 80.0 70 | South:...................... 0.0 71 | East:.. .................... 120.0 72 | West:....................... 0.0 73 | Top:........................ 0.0 74 | Bottom:..................... 0.0 75 >>> rmap.absolute_time.print_info() 76 +-------------------- Absolute time -----------------------------------------+ 77 | Start time:................. 1999-01-15 00:00:00 78 | End time:................... None 79 >>> rmap.metadata.print_info() 80 +-------------------- Metadata information ----------------------------------+ 81 | Datatype:................... CELL 82 | Number of columns:.......... 8 83 | Number of rows:............. 12 84 | Number of cells:............ 96 85 | North-South resolution:..... 10.0 86 | East-west resolution:....... 10.0 87 | Minimum value:.............. 1.0 88 | Maximum value:.............. 1.0 89 90 >>> grass.run_command("r.timestamp", map="strds_map_test_case", 91 ... date="2 years", quiet=True) 92 0 93 >>> rmap.read_timestamp_from_grass() 94 True 95 >>> rmap.get_temporal_extent_as_tuple() 96 (2, None) 97 >>> rmap.get_relative_time_unit() 98 'years' 99 >>> rmap.is_in_db() 100 False 101 >>> rmap.is_stds() 102 False 103 104 >>> newmap = rmap.get_new_instance("new@PERMANENT") 105 >>> isinstance(newmap, RasterDataset) 106 True 107 >>> newstrds = rmap.get_new_stds_instance("new@PERMANENT") 108 >>> isinstance(newstrds, SpaceTimeRasterDataset) 109 True 110 >>> rmap.get_type() 111 'raster' 112 >>> rmap.set_absolute_time(start_time=datetime(2001,1,1), 113 ... end_time=datetime(2012,1,1)) 114 True 115 >>> rmap.get_absolute_time() 116 (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0)) 117 >>> rmap.get_temporal_extent_as_tuple() 118 (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0)) 119 >>> rmap.get_name() 120 'strds_map_test_case' 121 >>> rmap.get_mapset() == mapset 122 True 123 >>> rmap.get_temporal_type() 124 'absolute' 125 >>> rmap.get_spatial_extent_as_tuple() 126 (80.0, 0.0, 120.0, 0.0, 0.0, 0.0) 127 >>> rmap.is_time_absolute() 128 True 129 >>> rmap.is_time_relative() 130 False 131 132 >>> grass.run_command("g.remove", flags="f", type="raster", name=name, quiet=True) 133 0 134 >>> grass.del_temp_region() 135 136 """ 137 def __init__(self, ident): 138 AbstractMapDataset.__init__(self) 139 self.reset(ident) 140 141 def is_stds(self): 142 """Return True if this class is a space time dataset 143 144 :return: True if this class is a space time dataset, False otherwise 145 """ 146 return False 147 148 def get_type(self): 149 return 'raster' 150 151 def get_new_instance(self, ident): 152 """Return a new instance with the type of this class""" 153 return RasterDataset(ident) 154 155 def get_new_stds_instance(self, ident): 156 """Return a new space time dataset instance in which maps 157 are stored with the type of this class""" 158 return SpaceTimeRasterDataset(ident) 159 160 def spatial_overlapping(self, dataset): 161 """Return True if the spatial extents 2d overlap""" 162 return self.spatial_extent.overlapping_2d(dataset.spatial_extent) 163 164 def spatial_relation(self, dataset): 165 """Return the two dimensional spatial relation""" 166 return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) 167 168 def spatial_intersection(self, dataset): 169 """Return the two dimensional intersection as spatial_extent 170 object or None in case no intersection was found. 171 172 :param dataset: The abstract dataset to intersect with 173 :return: The intersection spatial extent or None 174 """ 175 return self.spatial_extent.intersect_2d(dataset.spatial_extent) 176 177 def spatial_union(self, dataset): 178 """Return the two dimensional union as spatial_extent 179 object or None in case the extents does not overlap or meet. 180 181 :param dataset :The abstract dataset to create a union with 182 :return: The union spatial extent or None 183 """ 184 return self.spatial_extent.union_2d(dataset.spatial_extent) 185 186 def spatial_disjoint_union(self, dataset): 187 """Return the two dimensional union as spatial_extent object. 188 189 :param dataset: The abstract dataset to create a union with 190 :return: The union spatial extent 191 """ 192 return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) 193 194 def get_np_array(self): 195 """Return this raster map as memmap numpy style array to access the raster 196 values in numpy style without loading the whole map in the RAM. 197 198 In case this raster map does exists in the grass spatial database, 199 the map will be exported using r.out.bin to a temporary location 200 and assigned to the memmap object that is returned by this function. 201 202 In case the raster map does not exist, an empty temporary 203 binary file will be created and assigned to the memap object. 204 205 You need to call the write function to write the memmap 206 array back into grass. 207 """ 208 209 a = garray.array() 210 211 if self.map_exists(): 212 a.read(self.get_map_id()) 213 214 return a 215 216 def reset(self, ident): 217 """Reset the internal structure and set the identifier""" 218 self.base = RasterBase(ident=ident) 219 self.absolute_time = RasterAbsoluteTime(ident=ident) 220 self.relative_time = RasterRelativeTime(ident=ident) 221 self.spatial_extent = RasterSpatialExtent(ident=ident) 222 self.metadata = RasterMetadata(ident=ident) 223 self.stds_register = RasterSTDSRegister(ident=ident) 224 225 def has_grass_timestamp(self): 226 """Check if a grass file based time stamp exists for this map. 227 228 :return: True if success, False on error 229 """ 230 return self.ciface.has_raster_timestamp(self.get_name(), 231 self.get_mapset()) 232 233 def read_timestamp_from_grass(self): 234 """Read the timestamp of this map from the map metadata 235 in the grass file system based spatial database and 236 set the internal time stamp that should be insert/updated 237 in the temporal database. 238 239 :return: True if success, False on error 240 """ 241 242 if not self.has_grass_timestamp(): 243 return False 244 245 check, dates = self.ciface.read_raster_timestamp(self.get_name(), 246 self.get_mapset(),) 247 248 if check < 1: 249 self.msgr.error(_("Unable to read timestamp file " 250 "for raster map <%s>" % (self.get_map_id()))) 251 return False 252 253 if len(dates) == 2: 254 self.set_absolute_time(dates[0], dates[1]) 255 else: 256 self.set_relative_time(dates[0], dates[1], dates[2]) 257 258 return True 259 260 def write_timestamp_to_grass(self): 261 """Write the timestamp of this map into the map metadata in 262 the grass file system based spatial database. 263 264 Internally the libgis API functions are used for writing 265 266 :return: True if success, False on error 267 """ 268 check = self.ciface.write_raster_timestamp(self.get_name(), 269 self.get_mapset(), 270 self._convert_timestamp()) 271 272 if check == -1: 273 self.msgr.error(_("Unable to create timestamp file " 274 "for raster map <%s>" % (self.get_map_id()))) 275 return False 276 277 if check == -2: 278 self.msgr.error(_("Invalid datetime in timestamp for raster map " 279 "<%s>" % (self.get_map_id()))) 280 return False 281 282 if check == -3: 283 self.msgr.error(_("Internal error")) 284 return False 285 286 return True 287 288 def remove_timestamp_from_grass(self): 289 """Remove the timestamp from the grass file system based 290 spatial database 291 292 Internally the libgis API functions are used for removal 293 294 :return: True if success, False on error 295 """ 296 check = self.ciface.remove_raster_timestamp(self.get_name(), 297 self.get_mapset()) 298 299 if check == -1: 300 self.msgr.error(_("Unable to remove timestamp for raster map <%s>" 301 % (self.get_name()))) 302 return False 303 304 return True 305 306 def map_exists(self): 307 """Return True in case the map exists in the grass spatial database 308 309 :return: True if map exists, False otherwise 310 """ 311 return self.ciface.raster_map_exists(self.get_name(), 312 self.get_mapset()) 313 314 def load(self): 315 """Load all info from an existing raster map into the internal structure 316 317 This method checks first if the map exists, in case it exists 318 the metadata of the map is put into this object and True is returned 319 320 :return: True is the map exists and the metadata was filled 321 successfully and getting the data was successful, 322 False otherwise 323 """ 324 325 if self.map_exists() is not True: 326 return False 327 328 # Fill base information 329 self.base.set_creator(str(getpass.getuser())) 330 331 kvp = self.ciface.read_raster_info(self.get_name(), 332 self.get_mapset()) 333 334 if kvp: 335 # Fill spatial extent 336 self.set_spatial_extent_from_values(north=kvp["north"], 337 south=kvp["south"], 338 east=kvp["east"], 339 west=kvp["west"]) 340 341 # Fill metadata 342 self.metadata.set_nsres(kvp["nsres"]) 343 self.metadata.set_ewres(kvp["ewres"]) 344 self.metadata.set_datatype(kvp["datatype"]) 345 self.metadata.set_min(kvp["min"]) 346 self.metadata.set_max(kvp["max"]) 347 348 rows = int(kvp["rows"]) 349 cols = int(kvp["cols"]) 350 351 ncells = cols * rows 352 353 self.metadata.set_cols(cols) 354 self.metadata.set_rows(rows) 355 self.metadata.set_number_of_cells(ncells) 356 357 return True 358 359 return False 360 361############################################################################### 362 363 364class Raster3DDataset(AbstractMapDataset): 365 """Raster3d dataset class 366 367 This class provides functions to select, update, insert or delete raster3d 368 map information and valid time stamps into the SQL temporal database. 369 370 Usage: 371 372 .. code-block:: python 373 374 >>> import grass.script as grass 375 >>> init() 376 >>> grass.use_temp_region() 377 >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0, 378 ... t=100.0, b=0.0, res=10.0, res3=10.0) 379 0 380 >>> grass.run_command("r3.mapcalc", overwrite=True, quiet=True, 381 ... expression="str3ds_map_test_case = 1") 382 0 383 >>> grass.run_command("r3.timestamp", map="str3ds_map_test_case", 384 ... date="15 jan 1999", quiet=True) 385 0 386 >>> mapset = get_current_mapset() 387 >>> name = "str3ds_map_test_case" 388 >>> identifier = "%s@%s" % (name, mapset) 389 >>> r3map = Raster3DDataset(identifier) 390 >>> r3map.map_exists() 391 True 392 >>> r3map.read_timestamp_from_grass() 393 True 394 >>> r3map.get_temporal_extent_as_tuple() 395 (datetime.datetime(1999, 1, 15, 0, 0), None) 396 >>> r3map.load() 397 True 398 >>> r3map.spatial_extent.print_info() 399 +-------------------- Spatial extent ----------------------------------------+ 400 | North:...................... 80.0 401 | South:...................... 0.0 402 | East:.. .................... 120.0 403 | West:....................... 0.0 404 | Top:........................ 100.0 405 | Bottom:..................... 0.0 406 >>> r3map.absolute_time.print_info() 407 +-------------------- Absolute time -----------------------------------------+ 408 | Start time:................. 1999-01-15 00:00:00 409 | End time:................... None 410 >>> r3map.metadata.print_info() 411 +-------------------- Metadata information ----------------------------------+ 412 | Datatype:................... DCELL 413 | Number of columns:.......... 8 414 | Number of rows:............. 12 415 | Number of cells:............ 960 416 | North-South resolution:..... 10.0 417 | East-west resolution:....... 10.0 418 | Minimum value:.............. 1.0 419 | Maximum value:.............. 1.0 420 | Number of depths:........... 10 421 | Top-Bottom resolution:...... 10.0 422 423 >>> grass.run_command("r3.timestamp", map="str3ds_map_test_case", 424 ... date="2 years", quiet=True) 425 0 426 >>> r3map.read_timestamp_from_grass() 427 True 428 >>> r3map.get_temporal_extent_as_tuple() 429 (2, None) 430 >>> r3map.get_relative_time_unit() 431 'years' 432 >>> r3map.is_in_db() 433 False 434 >>> r3map.is_stds() 435 False 436 437 >>> newmap = r3map.get_new_instance("new@PERMANENT") 438 >>> isinstance(newmap, Raster3DDataset) 439 True 440 >>> newstr3ds = r3map.get_new_stds_instance("new@PERMANENT") 441 >>> isinstance(newstr3ds, SpaceTimeRaster3DDataset) 442 True 443 >>> r3map.get_type() 444 'raster3d' 445 >>> r3map.set_absolute_time(start_time=datetime(2001,1,1), 446 ... end_time=datetime(2012,1,1)) 447 True 448 >>> r3map.get_absolute_time() 449 (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0)) 450 >>> r3map.get_temporal_extent_as_tuple() 451 (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0)) 452 >>> r3map.get_name() 453 'str3ds_map_test_case' 454 >>> r3map.get_mapset() == mapset 455 True 456 >>> r3map.get_temporal_type() 457 'absolute' 458 >>> r3map.get_spatial_extent_as_tuple() 459 (80.0, 0.0, 120.0, 0.0, 100.0, 0.0) 460 >>> r3map.is_time_absolute() 461 True 462 >>> r3map.is_time_relative() 463 False 464 >>> grass.run_command("g.remove", flags="f", type="raster_3d", name=name, quiet=True) 465 0 466 >>> grass.del_temp_region() 467 468 """ 469 def __init__(self, ident): 470 AbstractMapDataset.__init__(self) 471 self.reset(ident) 472 473 def is_stds(self): 474 """Return True if this class is a space time dataset 475 476 :return: True if this class is a space time dataset, False otherwise 477 """ 478 return False 479 480 def get_type(self): 481 return "raster3d" 482 483 def get_new_instance(self, ident): 484 """Return a new instance with the type of this class""" 485 return Raster3DDataset(ident) 486 487 def get_new_stds_instance(self, ident): 488 """Return a new space time dataset instance in which maps 489 are stored with the type of this class""" 490 return SpaceTimeRaster3DDataset(ident) 491 492 def spatial_overlapping(self, dataset): 493 """Return True if the spatial extents overlap""" 494 if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": 495 return self.spatial_extent.overlapping(dataset.spatial_extent) 496 else: 497 return self.spatial_extent.overlapping_2d(dataset.spatial_extent) 498 499 def spatial_relation(self, dataset): 500 """Return the two or three dimensional spatial relation""" 501 if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": 502 return self.spatial_extent.spatial_relation(dataset.spatial_extent) 503 else: 504 return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) 505 506 def spatial_intersection(self, dataset): 507 """Return the three or two dimensional intersection as spatial_extent 508 object or None in case no intersection was found. 509 510 :param dataset: The abstract dataset to intersect with 511 :return: The intersection spatial extent or None 512 """ 513 if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": 514 return self.spatial_extent.intersect(dataset.spatial_extent) 515 else: 516 return self.spatial_extent.intersect_2d(dataset.spatial_extent) 517 518 def spatial_union(self, dataset): 519 """Return the three or two dimensional union as spatial_extent 520 object or None in case the extents does not overlap or meet. 521 522 :param dataset: The abstract dataset to create a union with 523 :return: The union spatial extent or None 524 """ 525 if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": 526 return self.spatial_extent.union(dataset.spatial_extent) 527 else: 528 return self.spatial_extent.union_2d(dataset.spatial_extent) 529 530 def spatial_disjoint_union(self, dataset): 531 """Return the three or two dimensional union as spatial_extent object. 532 533 :param dataset: The abstract dataset to create a union with 534 :return: The union spatial extent 535 """ 536 if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": 537 return self.spatial_extent.disjoint_union(dataset.spatial_extent) 538 else: 539 return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) 540 541 def get_np_array(self): 542 """Return this 3D raster map as memmap numpy style array to access the 543 3D raster values in numpy style without loading the whole map in 544 the RAM. 545 546 In case this 3D raster map does exists in the grass spatial database, 547 the map will be exported using r3.out.bin to a temporary location 548 and assigned to the memmap object that is returned by this function. 549 550 In case the 3D raster map does not exist, an empty temporary 551 binary file will be created and assigned to the memap object. 552 553 You need to call the write function to write the memmap 554 array back into grass. 555 """ 556 557 a = garray.array3d() 558 559 if self.map_exists(): 560 a.read(self.get_map_id()) 561 562 return a 563 564 def reset(self, ident): 565 """Reset the internal structure and set the identifier""" 566 self.base = Raster3DBase(ident=ident) 567 self.absolute_time = Raster3DAbsoluteTime(ident=ident) 568 self.relative_time = Raster3DRelativeTime(ident=ident) 569 self.spatial_extent = Raster3DSpatialExtent(ident=ident) 570 self.metadata = Raster3DMetadata(ident=ident) 571 self.stds_register = Raster3DSTDSRegister(ident=ident) 572 573 def has_grass_timestamp(self): 574 """Check if a grass file bsased time stamp exists for this map. 575 576 :return: True if success, False on error 577 """ 578 return self.ciface.has_raster3d_timestamp(self.get_name(), 579 self.get_mapset()) 580 581 def read_timestamp_from_grass(self): 582 """Read the timestamp of this map from the map metadata 583 in the grass file system based spatial database and 584 set the internal time stamp that should be insert/updated 585 in the temporal database. 586 587 :return: True if success, False on error 588 """ 589 590 if not self.has_grass_timestamp(): 591 return False 592 593 check, dates = self.ciface.read_raster3d_timestamp(self.get_name(), 594 self.get_mapset(),) 595 596 if check < 1: 597 self.msgr.error(_("Unable to read timestamp file " 598 "for 3D raster map <%s>" % (self.get_map_id()))) 599 return False 600 601 if len(dates) == 2: 602 self.set_absolute_time(dates[0], dates[1]) 603 else: 604 self.set_relative_time(dates[0], dates[1], dates[2]) 605 606 return True 607 608 def write_timestamp_to_grass(self): 609 """Write the timestamp of this map into the map metadata 610 in the grass file system based spatial database. 611 612 Internally the libgis API functions are used for writing 613 614 :return: True if success, False on error 615 """ 616 check = self.ciface.write_raster3d_timestamp(self.get_name(), 617 self.get_mapset(), 618 self._convert_timestamp()) 619 620 if check == -1: 621 self.msgr.error(_("Unable to create timestamp file " 622 "for 3D raster map <%s>" % (self.get_map_id()))) 623 return False 624 625 if check == -2: 626 self.msgr.error(_("Invalid datetime in timestamp for 3D raster " 627 "map <%s>" % (self.get_map_id()))) 628 return False 629 630 if check == -3: 631 self.msgr.error(_("Internal error")) 632 return False 633 634 return True 635 636 def remove_timestamp_from_grass(self): 637 """Remove the timestamp from the grass file system based spatial database 638 639 :return: True if success, False on error 640 """ 641 check = self.ciface.remove_raster3d_timestamp(self.get_name(), 642 self.get_mapset()) 643 644 if check == -1: 645 self.msgr.error(_("Unable to remove timestamp for raster map " 646 "<%s>" % (self.get_name()))) 647 return False 648 649 return True 650 651 def map_exists(self): 652 """Return True in case the map exists in the grass spatial database 653 654 :return: True if map exists, False otherwise 655 """ 656 return self.ciface.raster3d_map_exists(self.get_name(), 657 self.get_mapset()) 658 659 def load(self): 660 """Load all info from an existing 3d raster map into the internal structure 661 662 This method checks first if the map exists, in case it exists 663 the metadata of the map is put into this object and True is returned 664 665 :return: True is the map exists and the metadata was filled 666 successfully and getting the data was successful, 667 False otherwise 668 """ 669 670 if self.map_exists() is not True: 671 return False 672 673 # Fill base information 674 self.base.set_creator(str(getpass.getuser())) 675 676 # Fill spatial extent 677 kvp = self.ciface.read_raster3d_info(self.get_name(), 678 self.get_mapset()) 679 680 if kvp: 681 self.set_spatial_extent_from_values(north=kvp["north"], 682 south=kvp["south"], 683 east=kvp["east"], 684 west=kvp["west"], 685 top=kvp["top"], 686 bottom=kvp["bottom"]) 687 688 # Fill metadata 689 self.metadata.set_nsres(kvp["nsres"]) 690 self.metadata.set_ewres(kvp["ewres"]) 691 self.metadata.set_tbres(kvp["tbres"]) 692 self.metadata.set_datatype(kvp["datatype"]) 693 self.metadata.set_min(kvp["min"]) 694 self.metadata.set_max(kvp["max"]) 695 696 rows = int(kvp["rows"]) 697 cols = int(kvp["cols"]) 698 depths = int(kvp["depths"]) 699 700 ncells = cols * rows * depths 701 702 self.metadata.set_cols(cols) 703 self.metadata.set_rows(rows) 704 self.metadata.set_depths(depths) 705 self.metadata.set_number_of_cells(ncells) 706 707 return True 708 709 return False 710 711############################################################################### 712 713 714class VectorDataset(AbstractMapDataset): 715 """Vector dataset class 716 717 This class provides functions to select, update, insert or delete vector 718 map information and valid time stamps into the SQL temporal database. 719 720 Usage: 721 722 .. code-block:: python 723 724 >>> import grass.script as grass 725 >>> init() 726 >>> grass.use_temp_region() 727 >>> grass.run_command("g.region", n=80.0, s=0.0, e=120.0, w=0.0, 728 ... t=1.0, b=0.0, res=10.0) 729 0 730 >>> grass.run_command("v.random", overwrite=True, output="stvds_map_test_case", 731 ... n=100, zmin=0, zmax=100, flags="z", column="elevation", quiet=True) 732 0 733 >>> grass.run_command("v.timestamp", map="stvds_map_test_case", 734 ... date="15 jan 1999", quiet=True) 735 0 736 >>> mapset = get_current_mapset() 737 >>> name = "stvds_map_test_case" 738 >>> identifier = "%s@%s" % (name, mapset) 739 >>> vmap = VectorDataset(identifier) 740 >>> vmap.map_exists() 741 True 742 >>> vmap.read_timestamp_from_grass() 743 True 744 >>> vmap.get_temporal_extent_as_tuple() 745 (datetime.datetime(1999, 1, 15, 0, 0), None) 746 >>> vmap.load() 747 True 748 >>> vmap.absolute_time.print_info() 749 +-------------------- Absolute time -----------------------------------------+ 750 | Start time:................. 1999-01-15 00:00:00 751 | End time:................... None 752 >>> vmap.metadata.print_info() 753 +-------------------- Metadata information ----------------------------------+ 754 | Is map 3d .................. True 755 | Number of points ........... 100 756 | Number of lines ............ 0 757 | Number of boundaries ....... 0 758 | Number of centroids ........ 0 759 | Number of faces ............ 0 760 | Number of kernels .......... 0 761 | Number of primitives ....... 100 762 | Number of nodes ............ 0 763 | Number of areas ............ 0 764 | Number of islands .......... 0 765 | Number of holes ............ 0 766 | Number of volumes .......... 0 767 768 >>> grass.run_command("v.timestamp", map="stvds_map_test_case", 769 ... date="2 years", quiet=True) 770 0 771 >>> vmap.read_timestamp_from_grass() 772 True 773 >>> vmap.get_temporal_extent_as_tuple() 774 (2, None) 775 >>> vmap.get_relative_time_unit() 776 'years' 777 >>> vmap.is_in_db() 778 False 779 >>> vmap.is_stds() 780 False 781 782 >>> newmap = vmap.get_new_instance("new@PERMANENT") 783 >>> isinstance(newmap, VectorDataset) 784 True 785 >>> newstvds = vmap.get_new_stds_instance("new@PERMANENT") 786 >>> isinstance(newstvds, SpaceTimeVectorDataset) 787 True 788 >>> vmap.get_type() 789 'vector' 790 >>> vmap.set_absolute_time(start_time=datetime(2001,1,1), 791 ... end_time=datetime(2012,1,1)) 792 True 793 >>> vmap.get_absolute_time() 794 (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0)) 795 >>> vmap.get_temporal_extent_as_tuple() 796 (datetime.datetime(2001, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 0, 0)) 797 >>> vmap.get_name() 798 'stvds_map_test_case' 799 >>> vmap.get_mapset() == mapset 800 True 801 >>> vmap.get_temporal_type() 802 'absolute' 803 >>> vmap.is_time_absolute() 804 True 805 >>> vmap.is_time_relative() 806 False 807 >>> grass.run_command("g.remove", flags="f", type="vector", name=name, quiet=True) 808 0 809 >>> grass.del_temp_region() 810 811 """ 812 def __init__(self, ident): 813 AbstractMapDataset.__init__(self) 814 self.reset(ident) 815 816 def is_stds(self): 817 """Return True if this class is a space time dataset 818 819 :return: True if this class is a space time dataset, False otherwise 820 """ 821 return False 822 823 def get_type(self): 824 return "vector" 825 826 def get_new_instance(self, ident): 827 """Return a new instance with the type of this class""" 828 return VectorDataset(ident) 829 830 def get_new_stds_instance(self, ident): 831 """Return a new space time dataset instance in which maps 832 are stored with the type of this class""" 833 return SpaceTimeVectorDataset(ident) 834 835 def get_layer(self): 836 """Return the layer""" 837 return self.base.get_layer() 838 839 def spatial_overlapping(self, dataset): 840 """Return True if the spatial extents 2d overlap""" 841 842 return self.spatial_extent.overlapping_2d(dataset.spatial_extent) 843 844 def spatial_relation(self, dataset): 845 """Return the two dimensional spatial relation""" 846 847 return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) 848 849 def spatial_intersection(self, dataset): 850 """Return the two dimensional intersection as spatial_extent 851 object or None in case no intersection was found. 852 853 :param dataset: The abstract dataset to intersect with 854 :return: The intersection spatial extent or None 855 """ 856 return self.spatial_extent.intersect_2d(dataset.spatial_extent) 857 858 def spatial_union(self, dataset): 859 """Return the two dimensional union as spatial_extent 860 object or None in case the extents does not overlap or meet. 861 862 :param dataset: The abstract dataset to create a union with 863 :return: The union spatial extent or None 864 """ 865 return self.spatial_extent.union_2d(dataset.spatial_extent) 866 867 def spatial_disjoint_union(self, dataset): 868 """Return the two dimensional union as spatial_extent object. 869 870 :param dataset: The abstract dataset to create a union with 871 :return: The union spatial extent 872 """ 873 return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) 874 875 def reset(self, ident): 876 """Reset the internal structure and set the identifier""" 877 self.base = VectorBase(ident=ident) 878 self.absolute_time = VectorAbsoluteTime(ident=ident) 879 self.relative_time = VectorRelativeTime(ident=ident) 880 self.spatial_extent = VectorSpatialExtent(ident=ident) 881 self.metadata = VectorMetadata(ident=ident) 882 self.stds_register = VectorSTDSRegister(ident=ident) 883 884 def has_grass_timestamp(self): 885 """Check if a grass file bsased time stamp exists for this map. 886 """ 887 return self.ciface.has_vector_timestamp(self.get_name(), 888 self.get_mapset(), 889 self.get_layer()) 890 891 def read_timestamp_from_grass(self): 892 """Read the timestamp of this map from the map metadata 893 in the grass file system based spatial database and 894 set the internal time stamp that should be insert/updated 895 in the temporal database. 896 """ 897 898 if not self.has_grass_timestamp(): 899 return False 900 901 check, dates = self.ciface.read_vector_timestamp(self.get_name(), 902 self.get_mapset(),) 903 904 if check < 1: 905 self.msgr.error(_("Unable to read timestamp file " 906 "for vector map <%s>" % (self.get_map_id()))) 907 return False 908 909 if len(dates) == 2: 910 self.set_absolute_time(dates[0], dates[1]) 911 else: 912 self.set_relative_time(dates[0], dates[1], dates[2]) 913 914 return True 915 916 def write_timestamp_to_grass(self): 917 """Write the timestamp of this map into the map metadata in 918 the grass file system based spatial database. 919 920 Internally the libgis API functions are used for writing 921 """ 922 check = self.ciface.write_vector_timestamp(self.get_name(), 923 self.get_mapset(), 924 self._convert_timestamp(), 925 self.get_layer()) 926 927 if check == -1: 928 self.msgr.error(_("Unable to create timestamp file " 929 "for vector map <%s>" % (self.get_map_id()))) 930 return False 931 932 if check == -2: 933 self.msgr.error(_("Invalid datetime in timestamp for vector " 934 "map <%s>" % (self.get_map_id()))) 935 return False 936 937 return True 938 939 def remove_timestamp_from_grass(self): 940 """Remove the timestamp from the grass file system based spatial 941 database 942 943 Internally the libgis API functions are used for removal 944 """ 945 check = self.ciface.remove_vector_timestamp(self.get_name(), 946 self.get_mapset()) 947 948 if check == -1: 949 self.msgr.error(_("Unable to remove timestamp for vector " 950 "map <%s>" % (self.get_name()))) 951 return False 952 953 return True 954 955 def map_exists(self): 956 """Return True in case the map exists in the grass spatial database 957 958 :return: True if map exists, False otherwise 959 """ 960 return self.ciface.vector_map_exists(self.get_name(), 961 self.get_mapset()) 962 963 def load(self): 964 965 """Load all info from an existing vector map into the internal structure 966 967 This method checks first if the map exists, in case it exists 968 the metadata of the map is put into this object and True is returned 969 970 :return: True is the map exists and the metadata was filled 971 successfully and getting the data was successful, 972 False otherwise 973 """ 974 975 if self.map_exists() is not True: 976 return False 977 978 # Fill base information 979 self.base.set_creator(str(getpass.getuser())) 980 981 # Get the data from an existing vector map 982 983 kvp = self.ciface.read_vector_info(self.get_name(), 984 self.get_mapset()) 985 986 if kvp: 987 # Fill spatial extent 988 self.set_spatial_extent_from_values(north=kvp["north"], 989 south=kvp["south"], 990 east=kvp["east"], 991 west=kvp["west"], 992 top=kvp["top"], 993 bottom=kvp["bottom"]) 994 995 # Fill metadata 996 self.metadata.set_3d_info(kvp["map3d"]) 997 self.metadata.set_number_of_points(kvp["points"]) 998 self.metadata.set_number_of_lines(kvp["lines"]) 999 self.metadata.set_number_of_boundaries(kvp["boundaries"]) 1000 self.metadata.set_number_of_centroids(kvp["centroids"]) 1001 self.metadata.set_number_of_faces(kvp["faces"]) 1002 self.metadata.set_number_of_kernels(kvp["kernels"]) 1003 self.metadata.set_number_of_primitives(kvp["primitives"]) 1004 self.metadata.set_number_of_nodes(kvp["nodes"]) 1005 self.metadata.set_number_of_areas(kvp["areas"]) 1006 self.metadata.set_number_of_islands(kvp["islands"]) 1007 self.metadata.set_number_of_holes(kvp["holes"]) 1008 self.metadata.set_number_of_volumes(kvp["volumes"]) 1009 1010 return True 1011 1012 return False 1013 1014############################################################################### 1015 1016 1017class SpaceTimeRasterDataset(AbstractSpaceTimeDataset): 1018 """Space time raster dataset class 1019 1020 .. code-block:: python 1021 1022 >>> import grass.temporal as tgis 1023 >>> tgis.init() 1024 >>> mapset = tgis.get_current_mapset() 1025 >>> strds = tgis.SpaceTimeRasterDataset("old@%s"%mapset) 1026 >>> strds.is_in_db() 1027 False 1028 >>> strds.is_stds() 1029 True 1030 >>> strds.get_type() 1031 'strds' 1032 >>> newstrds = strds.get_new_instance("newstrds@%s"%mapset) 1033 >>> isinstance(newstrds, SpaceTimeRasterDataset) 1034 True 1035 >>> newmap = strds.get_new_map_instance("newmap@%s"%mapset) 1036 >>> isinstance(newmap, RasterDataset) 1037 True 1038 >>> strds.reset("new@%s"%mapset) 1039 >>> strds.is_in_db() 1040 False 1041 >>> strds.reset(None) 1042 >>> strds.is_in_db() 1043 False 1044 >>> strds.get_id() 1045 1046 ... 1047 """ 1048 def __init__(self, ident): 1049 AbstractSpaceTimeDataset.__init__(self, ident) 1050 1051 def is_stds(self): 1052 """Return True if this class is a space time dataset 1053 1054 :return: True if this class is a space time dataset, False otherwise 1055 """ 1056 return True 1057 1058 def get_type(self): 1059 return "strds" 1060 1061 def get_new_instance(self, ident): 1062 """Return a new instance with the type of this class""" 1063 return SpaceTimeRasterDataset(ident) 1064 1065 def get_new_map_instance(self, ident): 1066 """Return a new instance of a map dataset which is associated " 1067 "with the type of this class""" 1068 return RasterDataset(ident) 1069 1070 def get_map_register(self): 1071 """Return the name of the map register table""" 1072 return self.metadata.get_raster_register() 1073 1074 def set_map_register(self, name): 1075 """Set the name of the map register table""" 1076 self.metadata.set_raster_register(name) 1077 1078 def spatial_overlapping(self, dataset): 1079 """Return True if the spatial extents 2d overlap""" 1080 return self.spatial_extent.overlapping_2d(dataset.spatial_extent) 1081 1082 def spatial_relation(self, dataset): 1083 """Return the two dimensional spatial relation""" 1084 return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) 1085 1086 def spatial_intersection(self, dataset): 1087 """Return the two dimensional intersection as spatial_extent 1088 object or None in case no intersection was found. 1089 1090 :param dataset: The abstract dataset to intersect with 1091 :return: The intersection spatial extent or None 1092 """ 1093 return self.spatial_extent.intersect_2d(dataset.spatial_extent) 1094 1095 def spatial_union(self, dataset): 1096 """Return the two dimensional union as spatial_extent 1097 object or None in case the extents does not overlap or meet. 1098 1099 :param dataset: The abstract dataset to create a union with 1100 :return: The union spatial extent or None 1101 """ 1102 return self.spatial_extent.union_2d(dataset.spatial_extent) 1103 1104 def spatial_disjoint_union(self, dataset): 1105 """Return the two dimensional union as spatial_extent object. 1106 1107 :param dataset: The abstract dataset to create a union with 1108 :return: The union spatial extent 1109 """ 1110 return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) 1111 1112 def reset(self, ident): 1113 1114 """Reset the internal structure and set the identifier""" 1115 self.base = STRDSBase(ident=ident) 1116 self.base.set_creator(str(getpass.getuser())) 1117 self.absolute_time = STRDSAbsoluteTime(ident=ident) 1118 self.relative_time = STRDSRelativeTime(ident=ident) 1119 self.spatial_extent = STRDSSpatialExtent(ident=ident) 1120 self.metadata = STRDSMetadata(ident=ident) 1121 1122############################################################################### 1123 1124 1125class SpaceTimeRaster3DDataset(AbstractSpaceTimeDataset): 1126 """Space time raster3d dataset class 1127 1128 .. code-block:: python 1129 1130 >>> import grass.temporal as tgis 1131 >>> tgis.init() 1132 >>> mapset = tgis.get_current_mapset() 1133 >>> str3ds = tgis.SpaceTimeRaster3DDataset("old@%s"%mapset) 1134 >>> str3ds.is_in_db() 1135 False 1136 >>> str3ds.is_stds() 1137 True 1138 >>> str3ds.get_type() 1139 'str3ds' 1140 >>> newstrds = str3ds.get_new_instance("newstrds@%s"%mapset) 1141 >>> isinstance(newstrds, SpaceTimeRaster3DDataset) 1142 True 1143 >>> newmap = str3ds.get_new_map_instance("newmap@%s"%mapset) 1144 >>> isinstance(newmap, Raster3DDataset) 1145 True 1146 >>> str3ds.reset("new@%s"%mapset) 1147 >>> str3ds.is_in_db() 1148 False 1149 >>> str3ds.reset(None) 1150 >>> str3ds.is_in_db() 1151 False 1152 >>> str3ds.get_id() 1153 1154 ... 1155 """ 1156 1157 def __init__(self, ident): 1158 AbstractSpaceTimeDataset.__init__(self, ident) 1159 1160 def is_stds(self): 1161 """Return True if this class is a space time dataset 1162 1163 :return: True if this class is a space time dataset, False otherwise 1164 """ 1165 return True 1166 1167 def get_type(self): 1168 return "str3ds" 1169 1170 def get_new_instance(self, ident): 1171 """Return a new instance with the type of this class""" 1172 return SpaceTimeRaster3DDataset(ident) 1173 1174 def get_new_map_instance(self, ident): 1175 """Return a new instance of a map dataset which is associated 1176 with the type of this class""" 1177 return Raster3DDataset(ident) 1178 1179 def get_map_register(self): 1180 """Return the name of the map register table""" 1181 return self.metadata.get_raster3d_register() 1182 1183 def set_map_register(self, name): 1184 """Set the name of the map register table""" 1185 self.metadata.set_raster3d_register(name) 1186 1187 def spatial_overlapping(self, dataset): 1188 """Return True if the spatial extents overlap""" 1189 1190 if self.get_type() == dataset.get_type() or dataset.get_type() == "str3ds": 1191 return self.spatial_extent.overlapping(dataset.spatial_extent) 1192 else: 1193 return self.spatial_extent.overlapping_2d(dataset.spatial_extent) 1194 1195 def spatial_relation(self, dataset): 1196 """Return the two or three dimensional spatial relation""" 1197 1198 if self.get_type() == dataset.get_type() or \ 1199 dataset.get_type() == "str3ds": 1200 return self.spatial_extent.spatial_relation(dataset.spatial_extent) 1201 else: 1202 return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) 1203 1204 def spatial_intersection(self, dataset): 1205 """Return the three or two dimensional intersection as spatial_extent 1206 object or None in case no intersection was found. 1207 1208 :param dataset: The abstract dataset to intersect with 1209 :return: The intersection spatial extent or None 1210 """ 1211 if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d": 1212 return self.spatial_extent.intersect(dataset.spatial_extent) 1213 else: 1214 return self.spatial_extent.intersect_2d(dataset.spatial_extent) 1215 1216 def spatial_union(self, dataset): 1217 """Return the three or two dimensional union as spatial_extent 1218 object or None in case the extents does not overlap or meet. 1219 1220 :param dataset: The abstract dataset to create a union with 1221 :return: The union spatial extent or None 1222 """ 1223 if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d": 1224 return self.spatial_extent.union(dataset.spatial_extent) 1225 else: 1226 return self.spatial_extent.union_2d(dataset.spatial_extent) 1227 1228 def spatial_disjoint_union(self, dataset): 1229 """Return the three or two dimensional union as spatial_extent object. 1230 1231 :param dataset: The abstract dataset to create a union with 1232 :return: The union spatial extent 1233 """ 1234 if self.get_type() == dataset.get_type() or dataset.get_type() == "raster3d": 1235 return self.spatial_extent.disjoint_union(dataset.spatial_extent) 1236 else: 1237 return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) 1238 1239 def reset(self, ident): 1240 1241 """Reset the internal structure and set the identifier""" 1242 self.base = STR3DSBase(ident=ident) 1243 self.base.set_creator(str(getpass.getuser())) 1244 self.absolute_time = STR3DSAbsoluteTime(ident=ident) 1245 self.relative_time = STR3DSRelativeTime(ident=ident) 1246 self.spatial_extent = STR3DSSpatialExtent(ident=ident) 1247 self.metadata = STR3DSMetadata(ident=ident) 1248 1249############################################################################### 1250 1251 1252class SpaceTimeVectorDataset(AbstractSpaceTimeDataset): 1253 """Space time vector dataset class 1254 1255 .. code-block:: python 1256 1257 >>> import grass.temporal as tgis 1258 >>> tgis.init() 1259 >>> mapset = tgis.get_current_mapset() 1260 >>> stvds = tgis.SpaceTimeVectorDataset("old@%s"%mapset) 1261 >>> stvds.is_in_db() 1262 False 1263 >>> stvds.is_stds() 1264 True 1265 >>> stvds.get_type() 1266 'stvds' 1267 >>> newstvds = stvds.get_new_instance("newstvds@%s"%mapset) 1268 >>> isinstance(newstvds, SpaceTimeVectorDataset) 1269 True 1270 >>> newmap = stvds.get_new_map_instance("newmap@%s"%mapset) 1271 >>> isinstance(newmap, VectorDataset) 1272 True 1273 >>> stvds.reset("new@%s"%mapset) 1274 >>> stvds.is_in_db() 1275 False 1276 >>> stvds.reset(None) 1277 >>> stvds.is_in_db() 1278 False 1279 >>> stvds.get_id() 1280 1281 ... 1282 """ 1283 1284 def __init__(self, ident): 1285 AbstractSpaceTimeDataset.__init__(self, ident) 1286 1287 def is_stds(self): 1288 """Return True if this class is a space time dataset 1289 1290 :return: True if this class is a space time dataset, False otherwise 1291 """ 1292 return True 1293 1294 def get_type(self): 1295 return "stvds" 1296 1297 def get_new_instance(self, ident): 1298 """Return a new instance with the type of this class""" 1299 return SpaceTimeVectorDataset(ident) 1300 1301 def get_new_map_instance(self, ident): 1302 """Return a new instance of a map dataset which is associated 1303 with the type of this class""" 1304 return VectorDataset(ident) 1305 1306 def get_map_register(self): 1307 """Return the name of the map register table""" 1308 return self.metadata.get_vector_register() 1309 1310 def set_map_register(self, name): 1311 """Set the name of the map register table""" 1312 self.metadata.set_vector_register(name) 1313 1314 def spatial_overlapping(self, dataset): 1315 """Return True if the spatial extents 2d overlap""" 1316 return self.spatial_extent.overlapping_2d(dataset.spatial_extent) 1317 1318 def spatial_relation(self, dataset): 1319 """Return the two dimensional spatial relation""" 1320 return self.spatial_extent.spatial_relation_2d(dataset.spatial_extent) 1321 1322 def spatial_intersection(self, dataset): 1323 """Return the two dimensional intersection as spatial_extent 1324 object or None in case no intersection was found. 1325 1326 :param dataset: The abstract dataset to intersect with 1327 :return: The intersection spatial extent or None 1328 """ 1329 return self.spatial_extent.intersect_2d(dataset.spatial_extent) 1330 1331 def spatial_union(self, dataset): 1332 """Return the two dimensional union as spatial_extent 1333 object or None in case the extents does not overlap or meet. 1334 1335 :param dataset: The abstract dataset to create a union with 1336 :return: The union spatial extent or None 1337 """ 1338 return self.spatial_extent.union_2d(dataset.spatial_extent) 1339 1340 def spatial_disjoint_union(self, dataset): 1341 """Return the two dimensional union as spatial_extent object. 1342 1343 :param dataset: The abstract dataset to create a union with 1344 :return: The union spatial extent 1345 """ 1346 return self.spatial_extent.disjoint_union_2d(dataset.spatial_extent) 1347 1348 def reset(self, ident): 1349 1350 """Reset the internal structure and set the identifier""" 1351 self.base = STVDSBase(ident=ident) 1352 self.base.set_creator(str(getpass.getuser())) 1353 self.absolute_time = STVDSAbsoluteTime(ident=ident) 1354 self.relative_time = STVDSRelativeTime(ident=ident) 1355 self.spatial_extent = STVDSSpatialExtent(ident=ident) 1356 self.metadata = STVDSMetadata(ident=ident) 1357 1358############################################################################### 1359 1360if __name__ == "__main__": 1361 import doctest 1362 doctest.testmod() 1363