abstract.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. ## Define global variables to not exceed the 80 columns
  30. WARN_OVERWRITE = "Raster map <{0}> already exists and will be overwritten"
  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('elevation')
  44. >>> info.read()
  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. <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('elevation')
  171. >>> ele.name
  172. u'elevation'
  173. >>> ele.exist()
  174. True
  175. >>> ele.mapset
  176. 'PERMANENT'
  177. ..
  178. """
  179. self.mapset = mapset
  180. self._name = name
  181. ## Private attribute `_fd` that return the file descriptor of the map
  182. self._fd = None
  183. ## Private attribute `_rows` that return the number of rows
  184. # in active window, When the class is instanced is empty and it is set
  185. # when you open the file, using Rast_window_rows()
  186. self._rows = None
  187. ## Private attribute `_cols` that return the number of rows
  188. # in active window, When the class is instanced is empty and it is set
  189. # when you open the file, using Rast_window_cols()
  190. self._cols = None
  191. #self.region = Region()
  192. self.hist = History(self.name, self.mapset)
  193. self.cats = Category(self.name, self.mapset)
  194. self.info = Info(self.name, self.mapset)
  195. self._aopen = aopen
  196. self._kwopen = kwopen
  197. self._mtype = 'CELL'
  198. self._mode = 'r'
  199. self._overwrite = False
  200. def __enter__(self):
  201. self.open(*self._aopen, **self._kwopen)
  202. return self
  203. def __exit__(self, exc_type, exc_value, traceback):
  204. self.close()
  205. def _get_mtype(self):
  206. """Private method to get the Raster type"""
  207. return self._mtype
  208. def _set_mtype(self, mtype):
  209. """Private method to change the Raster type"""
  210. if mtype.upper() not in ('CELL', 'FCELL', 'DCELL'):
  211. #fatal(_("Raser type: {0} not supported".format(mtype) ) )
  212. str_err = "Raster type: {0} not supported ('CELL','FCELL','DCELL')"
  213. raise ValueError(_(str_err).format(mtype))
  214. self._mtype = mtype
  215. self._gtype = RTYPE[self.mtype]['grass type']
  216. mtype = property(fget=_get_mtype, fset=_set_mtype)
  217. def _get_mode(self):
  218. return self._mode
  219. def _set_mode(self, mode):
  220. if mode.upper() not in ('R', 'W'):
  221. str_err = _("Mode type: {0} not supported ('r', 'w')")
  222. raise ValueError(str_err.format(mode))
  223. self._mode = mode
  224. mode = property(fget=_get_mode, fset=_set_mode)
  225. def _get_overwrite(self):
  226. return self._overwrite
  227. def _set_overwrite(self, overwrite):
  228. if overwrite not in (True, False):
  229. str_err = _("Overwrite type: {0} not supported (True/False)")
  230. raise ValueError(str_err.format(overwrite))
  231. self._overwrite = overwrite
  232. overwrite = property(fget=_get_overwrite, fset=_set_overwrite)
  233. def _get_name(self):
  234. """Private method to return the Raster name"""
  235. return self._name
  236. def _set_name(self, newname):
  237. """Private method to change the Raster name"""
  238. if not utils.is_clean_name(newname):
  239. str_err = _("Map name {0} not valid")
  240. raise ValueError(str_err.format(newname))
  241. if self.exist():
  242. self.rename(newname)
  243. self._name = newname
  244. name = property(fget=_get_name, fset=_set_name)
  245. @must_be_open
  246. def _get_cats_title(self):
  247. return self.cats.title
  248. @must_be_open
  249. def _set_cats_title(self, newtitle):
  250. self.cats.title = newtitle
  251. cats_title = property(fget=_get_cats_title, fset=_set_cats_title)
  252. def __unicode__(self):
  253. return self.name_mapset()
  254. def __str__(self):
  255. """Return the string of the object"""
  256. return self.__unicode__()
  257. def __len__(self):
  258. return self._rows
  259. def __getitem__(self, key):
  260. """Return the row of Raster object, slice allowed."""
  261. if isinstance(key, slice):
  262. #import pdb; pdb.set_trace()
  263. #Get the start, stop, and step from the slice
  264. return (self.get_row(ii) for ii in range(*key.indices(len(self))))
  265. elif isinstance(key, tuple):
  266. x, y = key
  267. return self.get(x, y)
  268. elif isinstance(key, int):
  269. if key < 0: # Handle negative indices
  270. key += self._rows
  271. if key >= self._rows:
  272. fatal(INDXOUTRANGE.format(key))
  273. raise IndexError
  274. return self.get_row(key)
  275. else:
  276. fatal("Invalid argument type.")
  277. def __iter__(self):
  278. """Return a constructor of the class"""
  279. return (self.__getitem__(irow) for irow in range(self._rows))
  280. def _repr_png_(self):
  281. return raw_figure(utils.r_export(self))
  282. def exist(self):
  283. """Return True if the map already exist, and
  284. set the mapset if were not set.
  285. call the C function `G_find_raster`.
  286. >>> ele = RasterAbstractBase('elevation')
  287. >>> ele.exist()
  288. True
  289. """
  290. if self.name:
  291. if self.mapset == '':
  292. mapset = utils.get_mapset_raster(self.name, self.mapset)
  293. self.mapset = mapset if mapset else ''
  294. return True if mapset else False
  295. return bool(utils.get_mapset_raster(self.name, self.mapset))
  296. else:
  297. return False
  298. def is_open(self):
  299. """Return True if the map is open False otherwise.
  300. >>> ele = RasterAbstractBase('elevation')
  301. >>> ele.is_open()
  302. False
  303. """
  304. return True if self._fd is not None and self._fd >= 0 else False
  305. @must_be_open
  306. def close(self):
  307. """Close the map"""
  308. libraster.Rast_close(self._fd)
  309. # update rows and cols attributes
  310. self._rows = None
  311. self._cols = None
  312. self._fd = None
  313. def remove(self):
  314. """Remove the map"""
  315. if self.is_open():
  316. self.close()
  317. utils.remove(self.name, 'rast')
  318. def fullname(self):
  319. """Return the full name of a raster map: name@mapset"""
  320. return "{name}@{mapset}".format(name=self.name, mapset=self.mapset)
  321. def name_mapset(self, name=None, mapset=None):
  322. """Return the full name of the Raster.
  323. >>> ele = RasterAbstractBase('elevation')
  324. >>> ele.name_mapset()
  325. u'elevation@PERMANENT'
  326. """
  327. if name is None:
  328. name = self.name
  329. if mapset is None:
  330. self.exist()
  331. mapset = self.mapset
  332. gis_env = gisenv()
  333. if mapset and mapset != gis_env['MAPSET']:
  334. return "{name}@{mapset}".format(name=name, mapset=mapset)
  335. else:
  336. return name
  337. def rename(self, newname):
  338. """Rename the map"""
  339. if self.exist():
  340. utils.rename(self.name, newname, 'rast')
  341. self._name = newname
  342. def set_from_rast(self, rastname='', mapset=''):
  343. """Set the region that will use from a map, if rastername and mapset
  344. is not specify, use itself.
  345. call C function `Rast_get_cellhd`"""
  346. if self.is_open():
  347. fatal("You cannot change the region if map is open")
  348. raise
  349. region = Region()
  350. if rastname == '':
  351. rastname = self.name
  352. if mapset == '':
  353. mapset = self.mapset
  354. libraster.Rast_get_cellhd(rastname, mapset,
  355. ctypes.byref(region._region))
  356. # update rows and cols attributes
  357. self._rows = libraster.Rast_window_rows()
  358. self._cols = libraster.Rast_window_cols()
  359. @must_be_open
  360. def get_value(self, point, region=None):
  361. """This method returns the pixel value of a given pair of coordinates:
  362. :param point: pair of coordinates in tuple object
  363. """
  364. if not region:
  365. region = Region()
  366. row, col = utils.coor2pixel(point.coords(), region)
  367. if col < 0 or col > region.cols or row < 0 or row > region.rows:
  368. return None
  369. line = self.get_row(int(row))
  370. return line[int(col)]
  371. @must_be_open
  372. def has_cats(self):
  373. """Return True if the raster map has categories"""
  374. if self.exist():
  375. self.cats.read()
  376. if len(self.cats) != 0:
  377. return True
  378. return False
  379. @must_be_open
  380. def num_cats(self):
  381. """Return the number of categories"""
  382. return len(self.cats)
  383. @must_be_open
  384. def copy_cats(self, raster):
  385. """Copy categories from another raster map object"""
  386. self.cats.copy(raster.cats)
  387. @must_be_open
  388. def sort_cats(self):
  389. """Sort categories order by range"""
  390. self.cats.sort()
  391. @must_be_open
  392. def read_cats(self):
  393. """Read category from the raster map file"""
  394. self.cats.read(self)
  395. @must_be_open
  396. def write_cats(self):
  397. """Write category to the raster map file"""
  398. self.cats.write(self)
  399. @must_be_open
  400. def read_cats_rules(self, filename, sep=':'):
  401. """Read category from the raster map file"""
  402. self.cats.read_rules(filename, sep)
  403. @must_be_open
  404. def write_cats_rules(self, filename, sep=':'):
  405. """Write category to the raster map file"""
  406. self.cats.write_rules(filename, sep)
  407. @must_be_open
  408. def get_cats(self):
  409. """Return a category object"""
  410. cat = Category()
  411. cat.read(self)
  412. return cat
  413. @must_be_open
  414. def set_cats(self, category):
  415. """The internal categories are copied from this object."""
  416. self.cats.copy(category)
  417. @must_be_open
  418. def get_cat(self, label):
  419. """Return a category given an index or a label"""
  420. return self.cats[label]
  421. @must_be_open
  422. def set_cat(self, label, min_cat, max_cat=None, index=None):
  423. """Set or update a category"""
  424. self.cats.set_cat(index, (label, min_cat, max_cat))