1# -*- coding: utf-8 -*- 2""" 3Created on Fri Aug 17 16:05:25 2012 4 5@author: pietro 6""" 7from __future__ import (nested_scopes, generators, division, absolute_import, 8 with_statement, print_function, unicode_literals) 9import ctypes 10 11# 12# import GRASS modules 13# 14from grass.script import fatal, gisenv 15import grass.lib.gis as libgis 16import grass.lib.raster as libraster 17 18# 19# import pygrass modules 20# 21from grass.pygrass import utils 22from grass.pygrass.gis.region import Region 23from grass.pygrass.errors import must_be_open 24from grass.pygrass.shell.conversion import dict2html 25from grass.pygrass.shell.show import raw_figure 26 27# 28# import raster classes 29# 30from grass.pygrass.raster.raster_type import TYPE as RTYPE, RTYPE_STR 31from grass.pygrass.raster.category import Category 32from grass.pygrass.raster.history import History 33 34test_raster_name = "abstract_test_map" 35 36# Define global variables to not exceed the 80 columns 37INDXOUTRANGE = "The index (%d) is out of range, have you open the map?." 38INFO = """{name}@{mapset} 39rows: {rows} 40cols: {cols} 41north: {north} south: {south} nsres:{nsres} 42east: {east} west: {west} ewres:{ewres} 43range: {min}, {max} 44proj: {proj} 45""" 46 47 48class Info(object): 49 def __init__(self, name, mapset=''): 50 """Read the information for a raster map. :: 51 52 >>> info = Info(test_raster_name) 53 >>> info.read() 54 >>> info # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE 55 abstract_test_map@ 56 rows: 4 57 cols: 4 58 north: 40.0 south: 0.0 nsres:10.0 59 east: 40.0 west: 0.0 ewres:10.0 60 range: 11, 44 61 ... 62 <BLANKLINE> 63 64 """ 65 self.name = name 66 self.mapset = mapset 67 self.c_region = ctypes.pointer(libraster.struct_Cell_head()) 68 self.c_range = None 69 70 def _get_range(self): 71 if self.mtype == 'CELL': 72 self.c_range = ctypes.pointer(libraster.Range()) 73 libraster.Rast_read_range(self.name, self.mapset, self.c_range) 74 else: 75 self.c_range = ctypes.pointer(libraster.FPRange()) 76 libraster.Rast_read_fp_range(self.name, self.mapset, self.c_range) 77 78 def _get_raster_region(self): 79 libraster.Rast_get_cellhd(self.name, self.mapset, self.c_region) 80 81 def read(self): 82 self._get_range() 83 self._get_raster_region() 84 85 @property 86 def north(self): 87 return self.c_region.contents.north 88 89 @property 90 def south(self): 91 return self.c_region.contents.south 92 93 @property 94 def east(self): 95 return self.c_region.contents.east 96 97 @property 98 def west(self): 99 return self.c_region.contents.west 100 101 @property 102 def top(self): 103 return self.c_region.contents.top 104 105 @property 106 def bottom(self): 107 return self.c_region.contents.bottom 108 109 @property 110 def rows(self): 111 return self.c_region.contents.rows 112 113 @property 114 def cols(self): 115 return self.c_region.contents.cols 116 117 @property 118 def nsres(self): 119 return self.c_region.contents.ns_res 120 121 @property 122 def ewres(self): 123 return self.c_region.contents.ew_res 124 125 @property 126 def tbres(self): 127 return self.c_region.contents.tb_res 128 129 @property 130 def zone(self): 131 return self.c_region.contents.zone 132 133 @property 134 def proj(self): 135 return self.c_region.contents.proj 136 137 @property 138 def min(self): 139 if self.c_range is None: 140 return None 141 return self.c_range.contents.min 142 143 @property 144 def max(self): 145 if self.c_range is None: 146 return None 147 return self.c_range.contents.max 148 149 @property 150 def range(self): 151 if self.c_range is None: 152 return None, None 153 return self.c_range.contents.min, self.c_range.contents.max 154 155 @property 156 def mtype(self): 157 return RTYPE_STR[libraster.Rast_map_type(self.name, self.mapset)] 158 159 def _get_units(self): 160 return libraster.Rast_read_units(self.name, self.mapset) 161 162 def _set_units(self, units): 163 libraster.Rast_write_units(self.name, units) 164 165 units = property(_get_units, _set_units) 166 167 def _get_vdatum(self): 168 return libraster.Rast_read_vdatum(self.name, self.mapset) 169 170 def _set_vdatum(self, vdatum): 171 libraster.Rast_write_vdatum(self.name, vdatum) 172 173 vdatum = property(_get_vdatum, _set_vdatum) 174 175 def __repr__(self): 176 return INFO.format(name=self.name, mapset=self.mapset, 177 rows=self.rows, cols=self.cols, 178 north=self.north, south=self.south, 179 east=self.east, west=self.west, 180 top=self.top, bottom=self.bottom, 181 nsres=self.nsres, ewres=self.ewres, 182 tbres=self.tbres, zone=self.zone, 183 proj=self.proj, min=self.min, max=self.max) 184 185 def keys(self): 186 return ['name', 'mapset', 'rows', 'cols', 'north', 'south', 187 'east', 'west', 'top', 'bottom', 'nsres', 'ewres', 'tbres', 188 'zone', 'proj', 'min', 'max'] 189 190 def items(self): 191 return [(k, self.__getattribute__(k)) for k in self.keys()] 192 193 def __iter__(self): 194 return ((k, self.__getattribute__(k)) for k in self.keys()) 195 196 def _repr_html_(self): 197 return dict2html(dict(self.items()), keys=self.keys(), 198 border='1', kdec='b') 199 200 201class RasterAbstractBase(object): 202 """Raster_abstract_base: The base class from which all sub-classes 203 inherit. It does not implement any row or map access methods: 204 205 * Implements raster metadata information access (Type, ...) 206 * Implements an open method that will be overwritten by the sub-classes 207 * Implements the close method that might be overwritten by sub-classes 208 (should work for simple row access) 209 * Implements get and set region methods 210 * Implements color, history and category handling 211 * Renaming, deletion, ... 212 213 """ 214 def __init__(self, name, mapset="", *aopen, **kwopen): 215 """The constructor need at least the name of the map 216 *optional* field is the `mapset`. 217 218 >>> ele = RasterAbstractBase(test_raster_name) 219 >>> ele.name 220 'abstract_test_map' 221 >>> ele.exist() 222 True 223 224 .. 225 """ 226 self.mapset = mapset 227 self._name = name 228 # Private attribute `_fd` that return the file descriptor of the map 229 self._fd = None 230 # Private attribute `_rows` that return the number of rows 231 # in active window, When the class is instanced is empty and it is set 232 # when you open the file, using Rast_window_rows() 233 self._rows = None 234 # Private attribute `_cols` that return the number of rows 235 # in active window, When the class is instanced is empty and it is set 236 # when you open the file, using Rast_window_cols() 237 self._cols = None 238 # self.region = Region() 239 self.hist = History(self.name, self.mapset) 240 self.cats = Category(self.name, self.mapset) 241 self.info = Info(self.name, self.mapset) 242 self._aopen = aopen 243 self._kwopen = kwopen 244 self._mtype = 'CELL' 245 self._mode = 'r' 246 self._overwrite = False 247 248 def __enter__(self): 249 self.open(*self._aopen, **self._kwopen) 250 return self 251 252 def __exit__(self, exc_type, exc_value, traceback): 253 self.close() 254 255 def _get_mtype(self): 256 """Private method to get the Raster type""" 257 return self._mtype 258 259 def _set_mtype(self, mtype): 260 """Private method to change the Raster type""" 261 if mtype.upper() not in ('CELL', 'FCELL', 'DCELL'): 262 str_err = "Raster type: {0} not supported ('CELL','FCELL','DCELL')" 263 raise ValueError(_(str_err).format(mtype)) 264 self._mtype = mtype 265 self._gtype = RTYPE[self.mtype]['grass type'] 266 267 mtype = property(fget=_get_mtype, fset=_set_mtype) 268 269 def _get_mode(self): 270 return self._mode 271 272 def _set_mode(self, mode): 273 if mode.upper() not in ('R', 'W'): 274 str_err = _("Mode type: {0} not supported ('r', 'w')") 275 raise ValueError(str_err.format(mode)) 276 self._mode = mode 277 278 mode = property(fget=_get_mode, fset=_set_mode) 279 280 def _get_overwrite(self): 281 return self._overwrite 282 283 def _set_overwrite(self, overwrite): 284 if overwrite not in (True, False): 285 str_err = _("Overwrite type: {0} not supported (True/False)") 286 raise ValueError(str_err.format(overwrite)) 287 self._overwrite = overwrite 288 289 overwrite = property(fget=_get_overwrite, fset=_set_overwrite) 290 291 def _get_name(self): 292 """Private method to return the Raster name""" 293 return self._name 294 295 def _set_name(self, newname): 296 """Private method to change the Raster name""" 297 if not utils.is_clean_name(newname): 298 str_err = _("Map name {0} not valid") 299 raise ValueError(str_err.format(newname)) 300 if self.exist(): 301 self.rename(newname) 302 self._name = newname 303 304 name = property(fget=_get_name, fset=_set_name) 305 306 @must_be_open 307 def _get_cats_title(self): 308 return self.cats.title 309 310 @must_be_open 311 def _set_cats_title(self, newtitle): 312 self.cats.title = newtitle 313 314 cats_title = property(fget=_get_cats_title, fset=_set_cats_title) 315 316 def __unicode__(self): 317 return self.name_mapset() 318 319 def __str__(self): 320 """Return the string of the object""" 321 return self.__unicode__() 322 323 def __len__(self): 324 return self._rows 325 326 def __getitem__(self, key): 327 """Return the row of Raster object, slice allowed.""" 328 if isinstance(key, slice): 329 # Get the start, stop, and step from the slice 330 return (self.get_row(ii) for ii in range(*key.indices(len(self)))) 331 elif isinstance(key, tuple): 332 x, y = key 333 return self.get(x, y) 334 elif isinstance(key, int): 335 if not self.is_open(): 336 raise IndexError("Can not operate on a closed map. Call open() first.") 337 if key < 0: # Handle negative indices 338 key += self._rows 339 if key >= self._rows: 340 raise IndexError("The row index {0} is out of range [0, {1}).".format(key, self._rows)) 341 return self.get_row(key) 342 else: 343 fatal("Invalid argument type.") 344 345 def __iter__(self): 346 """Return a constructor of the class""" 347 return (self.__getitem__(irow) for irow in range(self._rows)) 348 349 def _repr_png_(self): 350 return raw_figure(utils.r_export(self)) 351 352 def exist(self): 353 """Return True if the map already exist, and 354 set the mapset if were not set. 355 356 call the C function `G_find_raster`. 357 358 >>> ele = RasterAbstractBase(test_raster_name) 359 >>> ele.exist() 360 True 361 """ 362 if self.name: 363 if self.mapset == '': 364 mapset = utils.get_mapset_raster(self.name, self.mapset) 365 self.mapset = mapset if mapset else '' 366 return True if mapset else False 367 return bool(utils.get_mapset_raster(self.name, self.mapset)) 368 else: 369 return False 370 371 def is_open(self): 372 """Return True if the map is open False otherwise. 373 374 >>> ele = RasterAbstractBase(test_raster_name) 375 >>> ele.is_open() 376 False 377 378 """ 379 return True if self._fd is not None and self._fd >= 0 else False 380 381 @must_be_open 382 def close(self): 383 """Close the map""" 384 libraster.Rast_close(self._fd) 385 # update rows and cols attributes 386 self._rows = None 387 self._cols = None 388 self._fd = None 389 390 def remove(self): 391 """Remove the map""" 392 if self.is_open(): 393 self.close() 394 utils.remove(self.name, 'rast') 395 396 def fullname(self): 397 """Return the full name of a raster map: name@mapset""" 398 return "{name}@{mapset}".format(name=self.name, mapset=self.mapset) 399 400 def name_mapset(self, name=None, mapset=None): 401 """Return the full name of the Raster. 402 403 >>> ele = RasterAbstractBase(test_raster_name) 404 >>> name = ele.name_mapset().split("@") 405 >>> name 406 ['abstract_test_map'] 407 408 """ 409 if name is None: 410 name = self.name 411 if mapset is None: 412 self.exist() 413 mapset = self.mapset 414 415 gis_env = gisenv() 416 417 if mapset and mapset != gis_env['MAPSET']: 418 return "{name}@{mapset}".format(name=name, mapset=mapset) 419 else: 420 return name 421 422 def rename(self, newname): 423 """Rename the map""" 424 if self.exist(): 425 utils.rename(self.name, newname, 'rast') 426 self._name = newname 427 428 def set_region_from_rast(self, rastname='', mapset=''): 429 """Set the computational region from a map, 430 if rastername and mapset is not specify, use itself. 431 This region will be used by all 432 raster map layers that are opened in the same process. 433 434 The GRASS region settings will not be modified. 435 436 call C function `Rast_get_cellhd`, `Rast_set_window` 437 438 """ 439 if self.is_open(): 440 fatal("You cannot change the region if map is open") 441 raise 442 region = Region() 443 if rastname == '': 444 rastname = self.name 445 if mapset == '': 446 mapset = self.mapset 447 448 libraster.Rast_get_cellhd(rastname, mapset, 449 region.byref()) 450 self._set_raster_window(region) 451 452 def set_region(self, region): 453 """Set the computational region that can be different from the 454 current region settings. This region will be used by all 455 raster map layers that are opened in the same process. 456 457 The GRASS region settings will not be modified. 458 """ 459 if self.is_open(): 460 fatal("You cannot change the region if map is open") 461 raise 462 self._set_raster_window(region) 463 464 def _set_raster_window(self, region): 465 libraster.Rast_set_window(region.byref()) 466 # update rows and cols attributes 467 self._rows = libraster.Rast_window_rows() 468 self._cols = libraster.Rast_window_cols() 469 470 @must_be_open 471 def get_value(self, point, region=None): 472 """This method returns the pixel value of a given pair of coordinates: 473 474 :param point: pair of coordinates in tuple object or class object with coords() method 475 """ 476 # Check for tuple 477 if type(point) != type([]) and type(point) != type(()): 478 point = point.coords() 479 480 if not region: 481 region = Region() 482 row, col = utils.coor2pixel(point, region) 483 if col < 0 or col > region.cols or row < 0 or row > region.rows: 484 return None 485 line = self.get_row(int(row)) 486 return line[int(col)] 487 488 @must_be_open 489 def has_cats(self): 490 """Return True if the raster map has categories""" 491 if self.exist(): 492 self.cats.read() 493 if len(self.cats) != 0: 494 return True 495 return False 496 497 @must_be_open 498 def num_cats(self): 499 """Return the number of categories""" 500 return len(self.cats) 501 502 @must_be_open 503 def copy_cats(self, raster): 504 """Copy categories from another raster map object""" 505 self.cats.copy(raster.cats) 506 507 @must_be_open 508 def sort_cats(self): 509 """Sort categories order by range""" 510 self.cats.sort() 511 512 @must_be_open 513 def read_cats(self): 514 """Read category from the raster map file""" 515 self.cats.read(self) 516 517 @must_be_open 518 def write_cats(self): 519 """Write category to the raster map file""" 520 self.cats.write(self) 521 522 @must_be_open 523 def read_cats_rules(self, filename, sep=':'): 524 """Read category from the raster map file""" 525 self.cats.read_rules(filename, sep) 526 527 @must_be_open 528 def write_cats_rules(self, filename, sep=':'): 529 """Write category to the raster map file""" 530 self.cats.write_rules(filename, sep) 531 532 @must_be_open 533 def get_cats(self): 534 """Return a category object""" 535 cat = Category(name=self.name, mapset=self.mapset) 536 cat.read() 537 return cat 538 539 @must_be_open 540 def set_cats(self, category): 541 """The internal categories are copied from this object.""" 542 self.cats.copy(category) 543 544 @must_be_open 545 def get_cat(self, label): 546 """Return a category given an index or a label""" 547 return self.cats[label] 548 549 @must_be_open 550 def set_cat(self, label, min_cat, max_cat=None, index=None): 551 """Set or update a category""" 552 self.cats.set_cat(index, (label, min_cat, max_cat)) 553 554if __name__ == "__main__": 555 556 import doctest 557 from grass.pygrass.modules import Module 558 Module("g.region", n=40, s=0, e=40, w=0, res=10) 559 Module("r.mapcalc", expression="%s = row() + (10 * col())" % (test_raster_name), 560 overwrite=True) 561 562 doctest.testmod() 563 564 """Remove the generated vector map, if exist""" 565 mset = utils.get_mapset_raster(test_raster_name, mapset='') 566 if mset: 567 Module("g.remove", flags='f', type='raster', name=test_raster_name) 568