array.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. """
  2. Functions to use GRASS 2D and 3D rasters with NumPy.
  3. Usage:
  4. >>> from __future__ import print_function
  5. >>> import grass.script as gs
  6. >>> from grass.script import array as garray
  7. >>>
  8. >>> # We create a temporary region that is only valid in this python session
  9. ... gs.use_temp_region()
  10. >>> gs.run_command("g.region", n=80, e=120, t=60, s=0, w=0, b=0, res=20, res3=20)
  11. >>>
  12. >>> # Lets create a raster map numpy array
  13. ... # based at the current region settings
  14. ... map2d_1 = garray.array()
  15. >>>
  16. >>> # Write some data
  17. ... for y in range(map2d_1.shape[0]):
  18. ... for x in range(map2d_1.shape[1]):
  19. ... map2d_1[y,x] = y + x
  20. ...
  21. >>> # Lets have a look at the array
  22. ... print(map2d_1)
  23. [[0. 1. 2. 3. 4. 5.]
  24. [1. 2. 3. 4. 5. 6.]
  25. [2. 3. 4. 5. 6. 7.]
  26. [3. 4. 5. 6. 7. 8.]]
  27. >>> # This will write the numpy array as GRASS raster map
  28. ... # with name map2d_1
  29. ... map2d_1.write(mapname="map2d_1", overwrite=True)
  30. 0
  31. >>>
  32. >>> # We create a new array from raster map2d_1 to modify it
  33. ... map2d_2 = garray.array(mapname="map2d_1")
  34. >>> # Don't do map2d_2 = map2d_1 % 3
  35. ... # because: this will overwrite the internal temporary filename
  36. ... map2d_2 %= 3
  37. >>> # Show the result
  38. ... print(map2d_2)
  39. [[0. 1. 2. 0. 1. 2.]
  40. [1. 2. 0. 1. 2. 0.]
  41. [2. 0. 1. 2. 0. 1.]
  42. [0. 1. 2. 0. 1. 2.]]
  43. >>> # Write the result as new raster map with name map2d_2
  44. ... map2d_2.write(mapname="map2d_2", overwrite=True)
  45. 0
  46. >>>
  47. >>> # Here we create a 3D raster map numpy array
  48. ... # based in the current region settings
  49. ... map3d_1 = garray.array3d()
  50. >>>
  51. >>> # Write some data
  52. ... # Note: the 3D array has map[depth][row][column] order
  53. ... for z in range(map3d_1.shape[0]):
  54. ... for y in range(map3d_1.shape[1]):
  55. ... for x in range(map3d_1.shape[2]):
  56. ... map3d_1[z,y,x] = z + y + x
  57. ...
  58. >>> # Lets have a look at the 3D array
  59. ... print(map3d_1)
  60. [[[ 0. 1. 2. 3. 4. 5.]
  61. [ 1. 2. 3. 4. 5. 6.]
  62. [ 2. 3. 4. 5. 6. 7.]
  63. [ 3. 4. 5. 6. 7. 8.]]
  64. <BLANKLINE>
  65. [[ 1. 2. 3. 4. 5. 6.]
  66. [ 2. 3. 4. 5. 6. 7.]
  67. [ 3. 4. 5. 6. 7. 8.]
  68. [ 4. 5. 6. 7. 8. 9.]]
  69. <BLANKLINE>
  70. [[ 2. 3. 4. 5. 6. 7.]
  71. [ 3. 4. 5. 6. 7. 8.]
  72. [ 4. 5. 6. 7. 8. 9.]
  73. [ 5. 6. 7. 8. 9. 10.]]]
  74. >>> # This will write the numpy array as GRASS 3D raster map
  75. ... # with name map3d_1
  76. ... map3d_1.write(mapname="map3d_1", overwrite=True)
  77. 0
  78. >>> # We create a new 3D array from 3D raster map3d_1 to modify it
  79. ... map3d_2 = garray.array3d(mapname="map3d_1")
  80. >>> # Don't do map3d_2 = map3d_1 % 3
  81. ... # because: this will overwrite the internal temporary filename
  82. ... map3d_2 %= 3
  83. >>> # Show the result
  84. ... print(map3d_2)
  85. [[[0. 1. 2. 0. 1. 2.]
  86. [1. 2. 0. 1. 2. 0.]
  87. [2. 0. 1. 2. 0. 1.]
  88. [0. 1. 2. 0. 1. 2.]]
  89. <BLANKLINE>
  90. [[1. 2. 0. 1. 2. 0.]
  91. [2. 0. 1. 2. 0. 1.]
  92. [0. 1. 2. 0. 1. 2.]
  93. [1. 2. 0. 1. 2. 0.]]
  94. <BLANKLINE>
  95. [[2. 0. 1. 2. 0. 1.]
  96. [0. 1. 2. 0. 1. 2.]
  97. [1. 2. 0. 1. 2. 0.]
  98. [2. 0. 1. 2. 0. 1.]]]
  99. >>> # Write the result as new 3D raster map with name map3d_2
  100. ... map3d_2.write(mapname="map3d_2", overwrite=True)
  101. 0
  102. (C) 2010-2012 by Glynn Clements and the GRASS Development Team
  103. This program is free software under the GNU General Public
  104. License (>=v2). Read the file COPYING that comes with GRASS
  105. for details.
  106. .. sectionauthor:: Glynn Clements
  107. """
  108. from __future__ import absolute_import
  109. import sys
  110. import numpy
  111. from .utils import try_remove
  112. from . import core as gcore
  113. from grass.exceptions import CalledModuleError
  114. ###############################################################################
  115. class _tempfile(object):
  116. def __init__(self, env=None):
  117. self.filename = gcore.tempfile(env=env)
  118. def __del__(self):
  119. try_remove(self.filename)
  120. ###############################################################################
  121. class array(numpy.memmap):
  122. def __new__(cls, mapname=None, null=None, dtype=numpy.double, env=None):
  123. """Define new numpy array
  124. :param cls:
  125. :param dtype: data type (default: numpy.double)
  126. :param env: environment
  127. """
  128. reg = gcore.region(env=env)
  129. r = reg["rows"]
  130. c = reg["cols"]
  131. shape = (r, c)
  132. tempfile = _tempfile(env)
  133. if mapname:
  134. kind = numpy.dtype(dtype).kind
  135. size = numpy.dtype(dtype).itemsize
  136. if kind == "f":
  137. flags = "f"
  138. elif kind in "biu":
  139. flags = "i"
  140. else:
  141. raise ValueError(_("Invalid kind <%s>") % kind)
  142. if size not in [1, 2, 4, 8]:
  143. raise ValueError(_("Invalid size <%d>") % size)
  144. gcore.run_command(
  145. "r.out.bin",
  146. flags=flags,
  147. input=mapname,
  148. output=tempfile.filename,
  149. bytes=size,
  150. null=null,
  151. quiet=True,
  152. overwrite=True,
  153. env=env,
  154. )
  155. self = numpy.memmap.__new__(
  156. cls, filename=tempfile.filename, dtype=dtype, mode="r+", shape=shape
  157. )
  158. self.tempfile = tempfile
  159. self.filename = tempfile.filename
  160. self._env = env
  161. return self
  162. def read(self, mapname, null=None):
  163. """Read raster map into array
  164. :param str mapname: name of raster map to be read
  165. :param null: null value
  166. :return: 0 on success
  167. :return: non-zero code on failure
  168. .. deprecated:: 7.1
  169. Instead reading the map after creating the array,
  170. pass the map name in the array constructor.
  171. """
  172. if sys.platform == "win32":
  173. gcore.warning(
  174. _(
  175. "grass.script.array.read is deprecated and does not"
  176. " work on MS Windows, pass raster name in the constructor"
  177. )
  178. )
  179. kind = self.dtype.kind
  180. size = self.dtype.itemsize
  181. if kind == "f":
  182. flags = "f"
  183. elif kind in "biu":
  184. flags = "i"
  185. else:
  186. raise ValueError(_("Invalid kind <%s>") % kind)
  187. if size not in [1, 2, 4, 8]:
  188. raise ValueError(_("Invalid size <%d>") % size)
  189. try:
  190. gcore.run_command(
  191. "r.out.bin",
  192. flags=flags,
  193. input=mapname,
  194. output=self.filename,
  195. bytes=size,
  196. null=null,
  197. quiet=True,
  198. overwrite=True,
  199. )
  200. except CalledModuleError:
  201. return 1
  202. else:
  203. return 0
  204. def write(self, mapname, title=None, null=None, overwrite=None, quiet=None):
  205. """Write array into raster map
  206. :param str mapname: name for raster map
  207. :param str title: title for raster map
  208. :param null: null value
  209. :param bool overwrite: True for overwritting existing raster maps
  210. :return: 0 on success
  211. :return: non-zero code on failure
  212. """
  213. kind = self.dtype.kind
  214. size = self.dtype.itemsize
  215. if kind == "f":
  216. if size == 4:
  217. flags = "f"
  218. elif size == 8:
  219. flags = "d"
  220. else:
  221. raise ValueError(_("Invalid FP size <%d>") % size)
  222. size = None
  223. elif kind in "biu":
  224. if size not in [1, 2, 4]:
  225. raise ValueError(_("Invalid integer size <%d>") % size)
  226. flags = None
  227. else:
  228. raise ValueError(_("Invalid kind <%s>") % kind)
  229. reg = gcore.region(env=self._env)
  230. try:
  231. gcore.run_command(
  232. "r.in.bin",
  233. flags=flags,
  234. input=self.filename,
  235. output=mapname,
  236. title=title,
  237. bytes=size,
  238. anull=null,
  239. overwrite=overwrite,
  240. quiet=quiet,
  241. north=reg["n"],
  242. south=reg["s"],
  243. east=reg["e"],
  244. west=reg["w"],
  245. rows=reg["rows"],
  246. cols=reg["cols"],
  247. env=self._env,
  248. )
  249. except CalledModuleError:
  250. return 1
  251. else:
  252. return 0
  253. ###############################################################################
  254. class array3d(numpy.memmap):
  255. def __new__(cls, mapname=None, null=None, dtype=numpy.double, env=None):
  256. """Define new 3d numpy array
  257. :param cls:
  258. :param dtype: data type (default: numpy.double)
  259. :param env: environment
  260. """
  261. reg = gcore.region(True)
  262. r = reg["rows3"]
  263. c = reg["cols3"]
  264. d = reg["depths"]
  265. shape = (d, r, c)
  266. tempfile = _tempfile()
  267. if mapname:
  268. kind = numpy.dtype(dtype).kind
  269. size = numpy.dtype(dtype).itemsize
  270. if kind == "f":
  271. flags = None # default is double
  272. elif kind in "biu":
  273. flags = "i"
  274. else:
  275. raise ValueError(_("Invalid kind <%s>") % kind)
  276. if size not in [1, 2, 4, 8]:
  277. raise ValueError(_("Invalid size <%d>") % size)
  278. gcore.run_command(
  279. "r3.out.bin",
  280. flags=flags,
  281. input=mapname,
  282. output=tempfile.filename,
  283. bytes=size,
  284. null=null,
  285. quiet=True,
  286. overwrite=True,
  287. env=env,
  288. )
  289. self = numpy.memmap.__new__(
  290. cls, filename=tempfile.filename, dtype=dtype, mode="r+", shape=shape
  291. )
  292. self.tempfile = tempfile
  293. self.filename = tempfile.filename
  294. self._env = env
  295. return self
  296. def read(self, mapname, null=None):
  297. """Read 3D raster map into array
  298. :param str mapname: name of 3D raster map to be read
  299. :param null: null value
  300. :return: 0 on success
  301. :return: non-zero code on failure
  302. .. deprecated:: 7.1
  303. Instead reading the map after creating the array,
  304. pass the map name in the array constructor.
  305. """
  306. if sys.platform == "win32":
  307. gcore.warning(
  308. _(
  309. "grass.script.array3d.read is deprecated and does not"
  310. " work on MS Windows, pass 3D raster name in the constructor"
  311. )
  312. )
  313. kind = self.dtype.kind
  314. size = self.dtype.itemsize
  315. if kind == "f":
  316. flags = None # default is double
  317. elif kind in "biu":
  318. flags = "i"
  319. else:
  320. raise ValueError(_("Invalid kind <%s>") % kind)
  321. if size not in [1, 2, 4, 8]:
  322. raise ValueError(_("Invalid size <%d>") % size)
  323. try:
  324. gcore.run_command(
  325. "r3.out.bin",
  326. flags=flags,
  327. input=mapname,
  328. output=self.filename,
  329. bytes=size,
  330. null=null,
  331. quiet=True,
  332. overwrite=True,
  333. )
  334. except CalledModuleError:
  335. return 1
  336. else:
  337. return 0
  338. def write(self, mapname, null=None, overwrite=None, quiet=None):
  339. """Write array into 3D raster map
  340. :param str mapname: name for 3D raster map
  341. :param null: null value
  342. :param bool overwrite: True for overwriting existing raster maps
  343. :return: 0 on success
  344. :return: non-zero code on failure
  345. """
  346. kind = self.dtype.kind
  347. size = self.dtype.itemsize
  348. flags = None
  349. if kind == "f":
  350. if size != 4 and size != 8:
  351. raise ValueError(_("Invalid FP size <%d>") % size)
  352. elif kind in "biu":
  353. if size not in [1, 2, 4, 8]:
  354. raise ValueError(_("Invalid integer size <%d>") % size)
  355. flags = "i"
  356. else:
  357. raise ValueError(_("Invalid kind <%s>") % kind)
  358. reg = gcore.region(True, env=self._env)
  359. try:
  360. gcore.run_command(
  361. "r3.in.bin",
  362. flags=flags,
  363. input=self.filename,
  364. output=mapname,
  365. bytes=size,
  366. null=null,
  367. overwrite=overwrite,
  368. quiet=quiet,
  369. north=reg["n"],
  370. south=reg["s"],
  371. top=reg["t"],
  372. bottom=reg["b"],
  373. east=reg["e"],
  374. west=reg["w"],
  375. depths=reg["depths"],
  376. rows=reg["rows3"],
  377. cols=reg["cols3"],
  378. env=self._env,
  379. )
  380. except CalledModuleError:
  381. return 1
  382. else:
  383. return 0