abstract.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Fri Aug 17 16:05:25 2012
  4. @author: pietro
  5. """
  6. import ctypes
  7. #
  8. # import GRASS modules
  9. #
  10. from grass.script import fatal, gisenv
  11. import grass.lib.gis as libgis
  12. import grass.lib.raster as libraster
  13. #
  14. # import pygrass modules
  15. #
  16. from grass.pygrass import functions
  17. from grass.pygrass.gis.region import Region
  18. from grass.pygrass.errors import must_be_open
  19. #
  20. # import raster classes
  21. #
  22. from raster_type import TYPE as RTYPE
  23. from category import Category
  24. from history import History
  25. ## Define global variables to not exceed the 80 columns
  26. WARN_OVERWRITE = "Raster map <{0}> already exists and will be overwritten"
  27. INDXOUTRANGE = "The index (%d) is out of range, have you open the map?."
  28. INFO = """{name}@{mapset}
  29. rows: {rows}
  30. cols: {cols}
  31. north: {north} south: {south} nsres:{nsres}
  32. east: {east} west: {west} ewres:{ewres}
  33. range: {min}, {max}
  34. proj: {proj}
  35. """
  36. class Info(object):
  37. def __init__(self, name, mapset=''):
  38. """Read the information for a raster map. ::
  39. >>> info = Info('elevation')
  40. >>> info # doctest: +ELLIPSIS
  41. elevation@
  42. rows: 1350
  43. cols: 1500
  44. north: 228500.0 south: 215000.0 nsres:10.0
  45. east: 645000.0 west: 630000.0 ewres:10.0
  46. range: 56, 156
  47. proj: 99
  48. ...
  49. """
  50. self.name = name
  51. self.mapset = mapset
  52. self.c_region = ctypes.pointer(libgis.Cell_head())
  53. libraster.Rast_get_cellhd(name, mapset,
  54. self.c_region)
  55. self._get_range()
  56. def _get_range(self):
  57. self.c_range = ctypes.pointer(libraster.Range())
  58. libraster.Rast_read_range(self.name, self.mapset, self.c_range)
  59. @property
  60. def north(self):
  61. return self.c_region.contents.north
  62. @property
  63. def south(self):
  64. return self.c_region.contents.south
  65. @property
  66. def east(self):
  67. return self.c_region.contents.east
  68. @property
  69. def west(self):
  70. return self.c_region.contents.west
  71. @property
  72. def top(self):
  73. return self.c_region.contents.top
  74. @property
  75. def bottom(self):
  76. return self.c_region.contents.bottom
  77. @property
  78. def rows(self):
  79. return self.c_region.contents.rows
  80. @property
  81. def cols(self):
  82. return self.c_region.contents.cols
  83. @property
  84. def nsres(self):
  85. return self.c_region.contents.ns_res
  86. @property
  87. def ewres(self):
  88. return self.c_region.contents.ew_res
  89. @property
  90. def tbres(self):
  91. return self.c_region.contents.tb_res
  92. @property
  93. def zone(self):
  94. return self.c_region.contents.zone
  95. @property
  96. def proj(self):
  97. return self.c_region.contents.proj
  98. @property
  99. def min(self):
  100. return self.c_range.contents.min
  101. @property
  102. def max(self):
  103. return self.c_range.contents.max
  104. @property
  105. def range(self):
  106. return self.c_range.contents.min, self.c_range.contents.max
  107. def __repr__(self):
  108. return INFO.format(name=self.name, mapset=self.mapset,
  109. rows=self.rows, cols=self.cols,
  110. north=self.north, south=self.south,
  111. east=self.east, west=self.west,
  112. top=self.top, bottom=self.bottom,
  113. nsres=self.nsres, ewres=self.ewres,
  114. tbres=self.tbres, zone=self.zone,
  115. proj=self.proj, min=self.min, max=self.max)
  116. class RasterAbstractBase(object):
  117. """Raster_abstract_base: The base class from which all sub-classes
  118. inherit. It does not implement any row or map access methods:
  119. * Implements raster metadata information access (Type, ...)
  120. * Implements an open method that will be overwritten by the sub-classes
  121. * Implements the close method that might be overwritten by sub-classes
  122. (should work for simple row access)
  123. * Implements get and set region methods
  124. * Implements color, history and category handling
  125. * Renaming, deletion, ...
  126. """
  127. def __init__(self, name, mapset=""):
  128. """The constructor need at least the name of the map
  129. *optional* field is the `mapset`. ::
  130. >>> ele = RasterAbstractBase('elevation')
  131. >>> ele.name
  132. 'elevation'
  133. >>> ele.exist()
  134. True
  135. >>> ele.mapset
  136. 'PERMANENT'
  137. ..
  138. """
  139. self.mapset = mapset
  140. self._name = name
  141. ## Private attribute `_fd` that return the file descriptor of the map
  142. self._fd = None
  143. ## Private attribute `_rows` that return the number of rows
  144. # in active window, When the class is instanced is empty and it is set
  145. # when you open the file, using Rast_window_rows()
  146. self._rows = None
  147. ## Private attribute `_cols` that return the number of rows
  148. # in active window, When the class is instanced is empty and it is set
  149. # when you open the file, using Rast_window_cols()
  150. self._cols = None
  151. #self.region = Region()
  152. self.cats = Category()
  153. self.hist = History()
  154. if self.exist():
  155. self.info = Info(self.name, self.mapset)
  156. def __enter__(self):
  157. if self.exist():
  158. self.open('r')
  159. return self
  160. else:
  161. raise ValueError("Raster not found.")
  162. def __exit__(self, exc_type, exc_value, traceback):
  163. self.close()
  164. def _get_mtype(self):
  165. """Private method to get the Raster type"""
  166. return self._mtype
  167. def _set_mtype(self, mtype):
  168. """Private method to change the Raster type"""
  169. if mtype.upper() not in ('CELL', 'FCELL', 'DCELL'):
  170. #fatal(_("Raser type: {0} not supported".format(mtype) ) )
  171. str_err = "Raster type: {0} not supported ('CELL','FCELL','DCELL')"
  172. raise ValueError(_(str_err).format(mtype))
  173. self._mtype = mtype
  174. self._gtype = RTYPE[self.mtype]['grass type']
  175. mtype = property(fget=_get_mtype, fset=_set_mtype)
  176. def _get_mode(self):
  177. return self._mode
  178. def _set_mode(self, mode):
  179. if mode.upper() not in ('R', 'W'):
  180. str_err = _("Mode type: {0} not supported ('r', 'w')")
  181. raise ValueError(str_err.format(mode))
  182. self._mode = mode
  183. mode = property(fget=_get_mode, fset=_set_mode)
  184. def _get_overwrite(self):
  185. return self._overwrite
  186. def _set_overwrite(self, overwrite):
  187. if overwrite not in (True, False):
  188. str_err = _("Overwrite type: {0} not supported (True/False)")
  189. raise ValueError(str_err.format(overwrite))
  190. self._overwrite = overwrite
  191. overwrite = property(fget=_get_overwrite, fset=_set_overwrite)
  192. def _get_name(self):
  193. """Private method to return the Raster name"""
  194. return self._name
  195. def _set_name(self, newname):
  196. """Private method to change the Raster name"""
  197. if not functions.is_clean_name(newname):
  198. str_err = _("Map name {0} not valid")
  199. raise ValueError(str_err.format(newname))
  200. if self.exist():
  201. self.rename(newname)
  202. self._name = newname
  203. name = property(fget=_get_name, fset=_set_name)
  204. @must_be_open
  205. def _get_cats_title(self):
  206. return self.cats.title
  207. @must_be_open
  208. def _set_cats_title(self, newtitle):
  209. self.cats.title = newtitle
  210. cats_title = property(fget=_get_cats_title, fset=_set_cats_title)
  211. def __unicode__(self):
  212. return self.name_mapset()
  213. def __str__(self):
  214. """Return the string of the object"""
  215. return self.__unicode__()
  216. def __len__(self):
  217. return self._rows
  218. def __getitem__(self, key):
  219. """Return the row of Raster object, slice allowed."""
  220. if isinstance(key, slice):
  221. #import pdb; pdb.set_trace()
  222. #Get the start, stop, and step from the slice
  223. return (self.get_row(ii) for ii in xrange(*key.indices(len(self))))
  224. elif isinstance(key, tuple):
  225. x, y = key
  226. return self.get(x, y)
  227. elif isinstance(key, int):
  228. if key < 0: # Handle negative indices
  229. key += self._rows
  230. if key >= self._rows:
  231. fatal(INDXOUTRANGE.format(key))
  232. raise IndexError
  233. return self.get_row(key)
  234. else:
  235. fatal("Invalid argument type.")
  236. def __iter__(self):
  237. """Return a constructor of the class"""
  238. return (self.__getitem__(irow) for irow in xrange(self._rows))
  239. def exist(self):
  240. """Return True if the map already exist, and
  241. set the mapset if were not set.
  242. call the C function `G_find_raster`.
  243. >>> ele = RasterAbstractBase('elevation')
  244. >>> ele.exist()
  245. True
  246. """
  247. if self.name:
  248. if self.mapset == '':
  249. mapset = functions.get_mapset_raster(self.name, self.mapset)
  250. self.mapset = mapset if mapset else ''
  251. return True if mapset else False
  252. return bool(functions.get_mapset_raster(self.name, self.mapset))
  253. else:
  254. return False
  255. def is_open(self):
  256. """Return True if the map is open False otherwise
  257. >>> ele = RasterAbstractBase('elevation')
  258. >>> ele.is_open()
  259. False
  260. """
  261. return True if self._fd is not None and self._fd >= 0 else False
  262. @must_be_open
  263. def close(self):
  264. """Close the map"""
  265. libraster.Rast_close(self._fd)
  266. # update rows and cols attributes
  267. self._rows = None
  268. self._cols = None
  269. self._fd = None
  270. def remove(self):
  271. """Remove the map"""
  272. if self.is_open():
  273. self.close()
  274. functions.remove(self.name, 'rast')
  275. def fullname(self):
  276. """Return the full name of a raster map: name@mapset"""
  277. return "{name}@{mapset}".format(name=self.name, mapset=self.mapset)
  278. def name_mapset(self, name=None, mapset=None):
  279. """Return the full name of the Raster
  280. >>> ele = RasterAbstractBase('elevation')
  281. >>> ele.name_mapset()
  282. 'elevation@PERMANENT'
  283. """
  284. if name is None:
  285. name = self.name
  286. if mapset is None:
  287. self.exist()
  288. mapset = self.mapset
  289. gis_env = gisenv()
  290. if mapset and mapset != gis_env['MAPSET']:
  291. return "{name}@{mapset}".format(name=name, mapset=mapset)
  292. else:
  293. return name
  294. def rename(self, newname):
  295. """Rename the map"""
  296. if self.exist():
  297. functions.rename(self.name, newname, 'rast')
  298. self._name = newname
  299. def set_from_rast(self, rastname='', mapset=''):
  300. """Set the region that will use from a map, if rastername and mapset
  301. is not specify, use itself.
  302. call C function `Rast_get_cellhd`"""
  303. if self.is_open():
  304. fatal("You cannot change the region if map is open")
  305. raise
  306. region = Region()
  307. if rastname == '':
  308. rastname = self.name
  309. if mapset == '':
  310. mapset = self.mapset
  311. libraster.Rast_get_cellhd(rastname, mapset,
  312. ctypes.byref(region._region))
  313. # update rows and cols attributes
  314. self._rows = libraster.Rast_window_rows()
  315. self._cols = libraster.Rast_window_cols()
  316. @must_be_open
  317. def get_value(self, point, region=None):
  318. """This method returns the pixel value of a given pair of coordinates:
  319. Parameters
  320. ------------
  321. point = pair of coordinates in tuple object
  322. """
  323. if not region:
  324. region = Region()
  325. row, col = functions.coor2pixel(point.coords(), region)
  326. if col < 0 or col > region.cols or row < 0 or row > region.rows:
  327. return None
  328. line = self.get_row(int(row))
  329. return line[int(col)]
  330. @must_be_open
  331. def has_cats(self):
  332. """Return True if the raster map has categories"""
  333. if self.exist():
  334. self.cats.read(self)
  335. self.close()
  336. if len(self.cats) != 0:
  337. return True
  338. return False
  339. @must_be_open
  340. def num_cats(self):
  341. """Return the number of categories"""
  342. return len(self.cats)
  343. @must_be_open
  344. def copy_cats(self, raster):
  345. """Copy categories from another raster map object"""
  346. self.cats.copy(raster.cats)
  347. @must_be_open
  348. def sort_cats(self):
  349. """Sort categories order by range"""
  350. self.cats.sort()
  351. @must_be_open
  352. def read_cats(self):
  353. """Read category from the raster map file"""
  354. self.cats.read(self)
  355. @must_be_open
  356. def write_cats(self):
  357. """Write category to the raster map file"""
  358. self.cats.write(self)
  359. @must_be_open
  360. def read_cats_rules(self, filename, sep=':'):
  361. """Read category from the raster map file"""
  362. self.cats.read_rules(filename, sep)
  363. @must_be_open
  364. def write_cats_rules(self, filename, sep=':'):
  365. """Write category to the raster map file"""
  366. self.cats.write_rules(filename, sep)
  367. @must_be_open
  368. def get_cats(self):
  369. """Return a category object"""
  370. cat = Category()
  371. cat.read(self)
  372. return cat
  373. @must_be_open
  374. def set_cats(self, category):
  375. """The internal categories are copied from this object."""
  376. self.cats.copy(category)
  377. @must_be_open
  378. def get_cat(self, label):
  379. """Return a category given an index or a label"""
  380. return self.cats[label]
  381. @must_be_open
  382. def set_cat(self, label, min_cat, max_cat=None, index=None):
  383. """Set or update a category"""
  384. self.cats.set_cat(index, (label, min_cat, max_cat))