array.py 9.6 KB

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