abstract.py 13 KB

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