abstract.py 17 KB

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