grid.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. from __future__ import (
  2. nested_scopes,
  3. generators,
  4. division,
  5. absolute_import,
  6. with_statement,
  7. print_function,
  8. unicode_literals,
  9. )
  10. import os
  11. import sys
  12. import multiprocessing as mltp
  13. import subprocess as sub
  14. import shutil as sht
  15. from grass.script.setup import write_gisrc
  16. from grass.script import append_node_pid, legalize_vector_name
  17. from grass.pygrass.gis import Mapset, Location
  18. from grass.pygrass.gis.region import Region
  19. from grass.pygrass.modules import Module
  20. from grass.pygrass.utils import get_mapset_raster, findmaps
  21. from grass.pygrass.modules.grid.split import split_region_tiles
  22. from grass.pygrass.modules.grid.patch import rpatch_map
  23. def select(parms, ptype):
  24. """Select only a certain type of parameters.
  25. :param parms: a DictType parameter with inputs or outputs of a Module class
  26. :type parms: DictType parameters
  27. :param ptype: String define the type of parameter that we want to select,
  28. valid ptype are: 'raster', 'vector', 'group'
  29. :type ptype: str
  30. :returns: An iterator with the value of the parameter.
  31. >>> slp = Module('r.slope.aspect',
  32. ... elevation='ele', slope='slp', aspect='asp',
  33. ... run_=False)
  34. >>> for rast in select(slp.outputs, 'raster'):
  35. ... print(rast)
  36. ...
  37. slp
  38. asp
  39. """
  40. for k in parms:
  41. par = parms[k]
  42. if par.type == ptype or par.typedesc == ptype and par.value:
  43. if par.multiple:
  44. for val in par.value:
  45. yield val
  46. else:
  47. yield par.value
  48. def copy_special_mapset_files(path_src, path_dst):
  49. """Copy all the special GRASS files that are contained in
  50. a mapset to another mapset
  51. :param path_src: the path to the original mapset
  52. :type path_src: str
  53. :param path_dst: the path to the new mapset
  54. :type path_dst: str
  55. """
  56. for fil in (fi for fi in os.listdir(path_src) if fi.isupper()):
  57. sht.copy(os.path.join(path_src, fil), path_dst)
  58. def copy_mapset(mapset, path):
  59. """Copy mapset to another place without copying raster and vector data.
  60. :param mapset: a Mapset instance to copy
  61. :type mapset: Mapset object
  62. :param path: path where the new mapset must be copied
  63. :type path: str
  64. :returns: the instance of the new Mapset.
  65. >>> from grass.script.core import gisenv
  66. >>> mname = gisenv()['MAPSET']
  67. >>> mset = Mapset()
  68. >>> mset.name == mname
  69. True
  70. >>> import tempfile as tmp
  71. >>> import os
  72. >>> path = os.path.join(tmp.gettempdir(), 'my_loc', 'my_mset')
  73. >>> copy_mapset(mset, path) # doctest: +ELLIPSIS
  74. Mapset(...)
  75. >>> sorted(os.listdir(path)) # doctest: +ELLIPSIS
  76. [...'PERMANENT'...]
  77. >>> sorted(os.listdir(os.path.join(path, 'PERMANENT')))
  78. ['DEFAULT_WIND', 'PROJ_INFO', 'PROJ_UNITS', 'VAR', 'WIND']
  79. >>> sorted(os.listdir(os.path.join(path, mname))) # doctest: +ELLIPSIS
  80. [...'SEARCH_PATH',...'WIND']
  81. >>> import shutil
  82. >>> shutil.rmtree(path)
  83. """
  84. per_old = os.path.join(mapset.gisdbase, mapset.location, "PERMANENT")
  85. per_new = os.path.join(path, "PERMANENT")
  86. map_old = mapset.path()
  87. map_new = os.path.join(path, mapset.name)
  88. if not os.path.isdir(per_new):
  89. os.makedirs(per_new)
  90. if not os.path.isdir(map_new):
  91. os.mkdir(map_new)
  92. copy_special_mapset_files(per_old, per_new)
  93. copy_special_mapset_files(map_old, map_new)
  94. gisdbase, location = os.path.split(path)
  95. return Mapset(mapset.name, location, gisdbase)
  96. def read_gisrc(gisrc):
  97. """Read a GISRC file and return a tuple with the mapset, location
  98. and gisdbase.
  99. :param gisrc: the path to GISRC file
  100. :type gisrc: str
  101. :returns: a tuple with the mapset, location and gisdbase
  102. >>> import os
  103. >>> from grass.script.core import gisenv
  104. >>> genv = gisenv()
  105. >>> (read_gisrc(os.environ['GISRC']) == (genv['MAPSET'],
  106. ... genv['LOCATION_NAME'],
  107. ... genv['GISDBASE']))
  108. True
  109. """
  110. with open(gisrc, "r") as gfile:
  111. gis = dict(
  112. [(k.strip(), v.strip()) for k, v in [row.split(":", 1) for row in gfile]]
  113. )
  114. return gis["MAPSET"], gis["LOCATION_NAME"], gis["GISDBASE"]
  115. def get_mapset(gisrc_src, gisrc_dst):
  116. """Get mapset from a GISRC source to a GISRC destination.
  117. :param gisrc_src: path to the GISRC source
  118. :type gisrc_src: str
  119. :param gisrc_dst: path to the GISRC destination
  120. :type gisrc_dst: str
  121. :returns: a tuple with Mapset(src), Mapset(dst)
  122. """
  123. msrc, lsrc, gsrc = read_gisrc(gisrc_src)
  124. mdst, ldst, gdst = read_gisrc(gisrc_dst)
  125. path_src = os.path.join(gsrc, lsrc, msrc)
  126. path_dst = os.path.join(gdst, ldst, mdst)
  127. if not os.path.isdir(path_dst):
  128. os.makedirs(path_dst)
  129. copy_special_mapset_files(path_src, path_dst)
  130. src = Mapset(msrc, lsrc, gsrc)
  131. dst = Mapset(mdst, ldst, gdst)
  132. visible = [m for m in src.visible]
  133. if src.name not in visible:
  134. visible.append(src.name)
  135. dst.visible.extend(visible)
  136. return src, dst
  137. def copy_groups(groups, gisrc_src, gisrc_dst, region=None):
  138. """Copy group from one mapset to another, crop the raster to the region
  139. :param groups: a list of strings with the group that must be copied
  140. from a master to another.
  141. :type groups: list of strings
  142. :param gisrc_src: path of the GISRC file from where we want to copy the groups
  143. :type gisrc_src: str
  144. :param gisrc_dst: path of the GISRC file where the groups will be created
  145. :type gisrc_dst: str
  146. :param region: a region like object or a dictionary with the region
  147. parameters that will be used to crop the rasters of the
  148. groups
  149. :type region: Region object or dictionary
  150. :returns: None
  151. """
  152. def rmloc(r):
  153. return r.split("@")[0] if "@" in r else r
  154. env = os.environ.copy()
  155. # instantiate modules
  156. get_grp = Module("i.group", flags="lg", stdout_=sub.PIPE, run_=False)
  157. set_grp = Module("i.group")
  158. get_grp.run_ = True
  159. src = read_gisrc(gisrc_src)
  160. dst = read_gisrc(gisrc_dst)
  161. rm = True if src[2] != dst[2] else False
  162. all_rasts = [r[0] for r in findmaps("raster", location=dst[1], gisdbase=dst[2])]
  163. for grp in groups:
  164. # change gisdbase to src
  165. env["GISRC"] = gisrc_src
  166. get_grp(group=grp, env_=env)
  167. rasts = [r for r in get_grp.outputs.stdout.split()]
  168. # change gisdbase to dst
  169. env["GISRC"] = gisrc_dst
  170. rast2cp = [r for r in rasts if rmloc(r) not in all_rasts]
  171. if rast2cp:
  172. copy_rasters(rast2cp, gisrc_src, gisrc_dst, region=region)
  173. set_grp(
  174. group=grp,
  175. input=[rmloc(r) for r in rasts] if rast2cp or rm else rasts,
  176. env_=env,
  177. )
  178. def set_region(region, gisrc_src, gisrc_dst, env):
  179. """Set a region into two different mapsets.
  180. :param region: a region like object or a dictionary with the region
  181. parameters that will be used to crop the rasters of the
  182. groups
  183. :type region: Region object or dictionary
  184. :param gisrc_src: path of the GISRC file from where we want to copy the groups
  185. :type gisrc_src: str
  186. :param gisrc_dst: path of the GISRC file where the groups will be created
  187. :type gisrc_dst: str
  188. :param env:
  189. :type env:
  190. :returns: None
  191. """
  192. reg_str = (
  193. "g.region n=%(north)r s=%(south)r "
  194. "e=%(east)r w=%(west)r "
  195. "nsres=%(nsres)r ewres=%(ewres)r"
  196. )
  197. reg_cmd = reg_str % dict(region.items())
  198. env["GISRC"] = gisrc_src
  199. sub.Popen(reg_cmd, shell=True, env=env)
  200. env["GISRC"] = gisrc_dst
  201. sub.Popen(reg_cmd, shell=True, env=env)
  202. def copy_rasters(rasters, gisrc_src, gisrc_dst, region=None):
  203. """Copy rasters from one mapset to another, crop the raster to the region.
  204. :param rasters: a list of strings with the raster map that must be copied
  205. from a master to another.
  206. :type rasters: list
  207. :param gisrc_src: path of the GISRC file from where we want to copy the groups
  208. :type gisrc_src: str
  209. :param gisrc_dst: path of the GISRC file where the groups will be created
  210. :type gisrc_dst: str
  211. :param region: a region like object or a dictionary with the region
  212. parameters that will be used to crop the rasters of the
  213. groups
  214. :type region: Region object or dictionary
  215. :returns: None
  216. """
  217. env = os.environ.copy()
  218. if region:
  219. set_region(region, gisrc_src, gisrc_dst, env)
  220. path_dst = os.path.join(*read_gisrc(gisrc_dst)[::-1])
  221. nam = "copy%d__%s" % (id(gisrc_dst), "%s")
  222. # instantiate modules
  223. mpclc = Module("r.mapcalc")
  224. rpck = Module("r.pack")
  225. rupck = Module("r.unpack")
  226. remove = Module("g.remove")
  227. for rast in rasters:
  228. rast_clean = rast.split("@")[0] if "@" in rast else rast
  229. # change gisdbase to src
  230. env["GISRC"] = gisrc_src
  231. name = nam % rast_clean
  232. mpclc(expression="%s=%s" % (name, rast), overwrite=True, env_=env)
  233. file_dst = "%s.pack" % os.path.join(path_dst, name)
  234. rpck(input=name, output=file_dst, overwrite=True, env_=env)
  235. remove(flags="f", type="raster", name=name, env_=env)
  236. # change gisdbase to dst
  237. env["GISRC"] = gisrc_dst
  238. rupck(input=file_dst, output=rast_clean, overwrite=True, env_=env)
  239. os.remove(file_dst)
  240. def copy_vectors(vectors, gisrc_src, gisrc_dst):
  241. """Copy vectors from one mapset to another, crop the raster to the region.
  242. :param vectors: a list of strings with the vector map that must be copied
  243. from a master to another.
  244. :type vectors: list
  245. :param gisrc_src: path of the GISRC file from where we want to copy the groups
  246. :type gisrc_src: str
  247. :param gisrc_dst: path of the GISRC file where the groups will be created
  248. :type gisrc_dst: str
  249. :returns: None
  250. """
  251. env = os.environ.copy()
  252. path_dst = os.path.join(*read_gisrc(gisrc_dst))
  253. nam = "copy%d__%s" % (id(gisrc_dst), "%s")
  254. # instantiate modules
  255. vpck = Module("v.pack")
  256. vupck = Module("v.unpack")
  257. remove = Module("g.remove")
  258. for vect in vectors:
  259. # change gisdbase to src
  260. env["GISRC"] = gisrc_src
  261. name = nam % vect
  262. file_dst = "%s.pack" % os.path.join(path_dst, name)
  263. vpck(input=name, output=file_dst, overwrite=True, env_=env)
  264. remove(flags="f", type="vector", name=name, env_=env)
  265. # change gisdbase to dst
  266. env["GISRC"] = gisrc_dst
  267. vupck(input=file_dst, output=vect, overwrite=True, env_=env)
  268. os.remove(file_dst)
  269. def get_cmd(cmdd):
  270. """Transform a cmd dictionary to a list of parameters. It is useful to
  271. pickle a Module class and cnvert into a string that can be used with
  272. `Popen(get_cmd(cmdd), shell=True)`.
  273. :param cmdd: a module dictionary with all the parameters
  274. :type cmdd: dict
  275. >>> slp = Module('r.slope.aspect',
  276. ... elevation='ele', slope='slp', aspect='asp',
  277. ... overwrite=True, run_=False)
  278. >>> get_cmd(slp.get_dict()) # doctest: +ELLIPSIS
  279. ['r.slope.aspect', 'elevation=ele', 'format=degrees', ..., '--o']
  280. """
  281. cmd = [
  282. cmdd["name"],
  283. ]
  284. cmd.extend(("%s=%s" % (k, v) for k, v in cmdd["inputs"] if not isinstance(v, list)))
  285. cmd.extend(
  286. (
  287. "%s=%s"
  288. % (
  289. k,
  290. ",".join(vals if isinstance(vals[0], str) else [repr(v) for v in vals]),
  291. )
  292. for k, vals in cmdd["inputs"]
  293. if isinstance(vals, list)
  294. )
  295. )
  296. cmd.extend(
  297. ("%s=%s" % (k, v) for k, v in cmdd["outputs"] if not isinstance(v, list))
  298. )
  299. cmd.extend(
  300. (
  301. "%s=%s" % (k, ",".join([repr(v) for v in vals]))
  302. for k, vals in cmdd["outputs"]
  303. if isinstance(vals, list)
  304. )
  305. )
  306. cmd.extend(("-%s" % (flg) for flg in cmdd["flags"] if len(flg) == 1))
  307. cmd.extend(("--%s" % (flg[0]) for flg in cmdd["flags"] if len(flg) > 1))
  308. return cmd
  309. def cmd_exe(args):
  310. """Create a mapset, and execute a cmd inside.
  311. :param args: is a tuple that contains several information see below
  312. :type args: tuple
  313. :returns: None
  314. The puple has to contain:
  315. - bbox (dict): a dict with the region parameters (n, s, e, w, etc.)
  316. that we want to set before to apply the command.
  317. - mapnames (dict): a dictionary to substitute the input if the domain has
  318. been split in several tiles.
  319. - gisrc_src (str): path of the GISRC file from where we want to copy the
  320. groups.
  321. - gisrc_dst (str): path of the GISRC file where the groups will be created.
  322. - cmd (dict): a dictionary with all the parameter of a GRASS module.
  323. - groups (list): a list of strings with the groups that we want to copy in
  324. the mapset.
  325. """
  326. bbox, mapnames, gisrc_src, gisrc_dst, cmd, groups = args
  327. src, dst = get_mapset(gisrc_src, gisrc_dst)
  328. env = os.environ.copy()
  329. env["GISRC"] = gisrc_dst
  330. shell = True if sys.platform == "win32" else False
  331. if mapnames:
  332. inputs = dict(cmd["inputs"])
  333. # reset the inputs to
  334. for key in mapnames:
  335. inputs[key] = mapnames[key]
  336. cmd["inputs"] = inputs.items()
  337. # set the region to the tile
  338. sub.Popen(["g.region", "raster=%s" % key], shell=shell, env=env).wait()
  339. else:
  340. # set the computational region
  341. lcmd = [
  342. "g.region",
  343. ]
  344. lcmd.extend(["%s=%s" % (k, v) for k, v in bbox.items()])
  345. sub.Popen(lcmd, shell=shell, env=env).wait()
  346. if groups:
  347. copy_groups(groups, gisrc_src, gisrc_dst)
  348. # run the grass command
  349. sub.Popen(get_cmd(cmd), shell=shell, env=env).wait()
  350. # remove temp GISRC
  351. os.remove(gisrc_dst)
  352. class GridModule(object):
  353. # TODO maybe also i.* could be supported easily
  354. """Run GRASS raster commands in a multiprocessing mode.
  355. :param cmd: raster GRASS command, only command staring with r.* are valid.
  356. :type cmd: str
  357. :param width: width of the tile, in pixel
  358. :type width: int
  359. :param height: height of the tile, in pixel.
  360. :type height: int
  361. :param overlap: overlap between tiles, in pixel.
  362. :type overlap: int
  363. :param processes: number of threads, default value is equal to the number
  364. of processor available.
  365. :param split: if True use r.tile to split all the inputs.
  366. :type split: bool
  367. :param mapset_prefix: if specified created mapsets start with this prefix
  368. :type mapset_prefix: str
  369. :param run_: if False only instantiate the object
  370. :type run_: bool
  371. :param args: give all the parameters to the command
  372. :param kargs: give all the parameters to the command
  373. >>> grd = GridModule('r.slope.aspect',
  374. ... width=500, height=500, overlap=2,
  375. ... processes=None, split=False,
  376. ... elevation='elevation',
  377. ... slope='slope', aspect='aspect', overwrite=True)
  378. >>> grd.run()
  379. Temporary mapsets created start with a generated prefix which is unique for each
  380. process (includes PID and node name). If more instances of this class are used in
  381. parallel from one process with the same module, a custom *mapset_prefix* needs to
  382. be provided.
  383. """
  384. def __init__(
  385. self,
  386. cmd,
  387. width=None,
  388. height=None,
  389. overlap=0,
  390. processes=None,
  391. split=False,
  392. debug=False,
  393. region=None,
  394. move=None,
  395. log=False,
  396. start_row=0,
  397. start_col=0,
  398. out_prefix="",
  399. mapset_prefix=None,
  400. *args,
  401. **kargs,
  402. ):
  403. kargs["run_"] = False
  404. self.mset = Mapset()
  405. self.module = Module(cmd, *args, **kargs)
  406. self.width = width
  407. self.height = height
  408. self.overlap = overlap
  409. self.processes = processes
  410. self.region = region if region else Region()
  411. self.start_row = start_row
  412. self.start_col = start_col
  413. self.out_prefix = out_prefix
  414. self.log = log
  415. self.move = move
  416. self.gisrc_src = os.environ["GISRC"]
  417. self.n_mset, self.gisrc_dst = None, None
  418. if self.move:
  419. self.n_mset = copy_mapset(self.mset, self.move)
  420. self.gisrc_dst = write_gisrc(
  421. self.n_mset.gisdbase, self.n_mset.location, self.n_mset.name
  422. )
  423. rasters = [r for r in select(self.module.inputs, "raster")]
  424. if rasters:
  425. copy_rasters(
  426. rasters, self.gisrc_src, self.gisrc_dst, region=self.region
  427. )
  428. vectors = [v for v in select(self.module.inputs, "vector")]
  429. if vectors:
  430. copy_vectors(vectors, self.gisrc_src, self.gisrc_dst)
  431. groups = [g for g in select(self.module.inputs, "group")]
  432. if groups:
  433. copy_groups(groups, self.gisrc_src, self.gisrc_dst, region=self.region)
  434. self.bboxes = split_region_tiles(
  435. region=region, width=width, height=height, overlap=overlap
  436. )
  437. if mapset_prefix:
  438. self.mapset_prefix = mapset_prefix
  439. else:
  440. self.mapset_prefix = append_node_pid("grid_" + legalize_vector_name(cmd))
  441. self.msetstr = self.mapset_prefix + "_%03d_%03d"
  442. self.inlist = None
  443. if split:
  444. self.split()
  445. self.debug = debug
  446. def __del__(self):
  447. if self.gisrc_dst:
  448. # remove GISRC file
  449. os.remove(self.gisrc_dst)
  450. def clean_location(self, location=None):
  451. """Remove all created mapsets.
  452. :param location: a Location instance where we are running the analysis
  453. :type location: Location object
  454. """
  455. if location is None:
  456. if self.n_mset:
  457. self.n_mset.current()
  458. location = Location()
  459. mapsets = location.mapsets(self.mapset_prefix + "_*")
  460. for mset in mapsets:
  461. Mapset(mset).delete()
  462. if self.n_mset and self.n_mset.is_current():
  463. self.mset.current()
  464. def split(self):
  465. """Split all the raster inputs using r.tile"""
  466. rtile = Module("r.tile")
  467. inlist = {}
  468. for inm in select(self.module.inputs, "raster"):
  469. rtile(
  470. input=inm.value,
  471. output=inm.value,
  472. width=self.width,
  473. height=self.height,
  474. overlap=self.overlap,
  475. )
  476. patt = "%s-*" % inm.value
  477. inlist[inm.value] = sorted(self.mset.glist(type="raster", pattern=patt))
  478. self.inlist = inlist
  479. def get_works(self):
  480. """Return a list of tuble with the parameters for cmd_exe function"""
  481. works = []
  482. reg = Region()
  483. if self.move:
  484. mdst, ldst, gdst = read_gisrc(self.gisrc_dst)
  485. else:
  486. ldst, gdst = self.mset.location, self.mset.gisdbase
  487. cmd = self.module.get_dict()
  488. groups = [g for g in select(self.module.inputs, "group")]
  489. for row, box_row in enumerate(self.bboxes):
  490. for col, box in enumerate(box_row):
  491. inms = None
  492. if self.inlist:
  493. inms = {}
  494. cols = len(box_row)
  495. for key in self.inlist:
  496. indx = row * cols + col
  497. inms[key] = "%s@%s" % (self.inlist[key][indx], self.mset.name)
  498. # set the computational region, prepare the region parameters
  499. bbox = dict([(k[0], str(v)) for k, v in box.items()[:-2]])
  500. bbox["nsres"] = "%f" % reg.nsres
  501. bbox["ewres"] = "%f" % reg.ewres
  502. new_mset = (
  503. self.msetstr % (self.start_row + row, self.start_col + col),
  504. )
  505. works.append(
  506. (
  507. bbox,
  508. inms,
  509. self.gisrc_src,
  510. write_gisrc(gdst, ldst, new_mset),
  511. cmd,
  512. groups,
  513. )
  514. )
  515. return works
  516. def define_mapset_inputs(self):
  517. """Add the mapset information to the input maps"""
  518. for inmap in self.module.inputs:
  519. inm = self.module.inputs[inmap]
  520. if inm.type in ("raster", "vector") and inm.value:
  521. if "@" not in inm.value:
  522. mset = get_mapset_raster(inm.value)
  523. inm.value = inm.value + "@%s" % mset
  524. def run(self, patch=True, clean=True):
  525. """Run the GRASS command
  526. :param patch: set False if you does not want to patch the results
  527. :type patch: bool
  528. :param clean: set False if you does not want to remove all the stuff
  529. created by GridModule
  530. :type clean: bool
  531. """
  532. self.module.flags.overwrite = True
  533. self.define_mapset_inputs()
  534. if self.debug:
  535. for wrk in self.get_works():
  536. cmd_exe(wrk)
  537. else:
  538. pool = mltp.Pool(processes=self.processes)
  539. result = pool.map_async(cmd_exe, self.get_works())
  540. result.wait()
  541. pool.close()
  542. pool.join()
  543. if not result.successful():
  544. raise RuntimeError(_("Execution of subprocesses was not successful"))
  545. if patch:
  546. if self.move:
  547. os.environ["GISRC"] = self.gisrc_dst
  548. self.n_mset.current()
  549. self.patch()
  550. os.environ["GISRC"] = self.gisrc_src
  551. self.mset.current()
  552. # copy the outputs from dst => src
  553. routputs = [
  554. self.out_prefix + o for o in select(self.module.outputs, "raster")
  555. ]
  556. copy_rasters(routputs, self.gisrc_dst, self.gisrc_src)
  557. else:
  558. self.patch()
  559. if self.log:
  560. # record in the temp directory
  561. from grass.lib.gis import G_tempfile
  562. tmp, dummy = os.path.split(G_tempfile())
  563. tmpdir = os.path.join(tmp, self.module.name)
  564. for k in self.module.outputs:
  565. par = self.module.outputs[k]
  566. if par.typedesc == "raster" and par.value:
  567. dirpath = os.path.join(tmpdir, par.name)
  568. if not os.path.isdir(dirpath):
  569. os.makedirs(dirpath)
  570. fil = open(os.path.join(dirpath, self.out_prefix + par.value), "w+")
  571. fil.close()
  572. if clean:
  573. self.clean_location()
  574. self.rm_tiles()
  575. if self.n_mset:
  576. gisdbase, location = os.path.split(self.move)
  577. self.clean_location(Location(location, gisdbase))
  578. # rm temporary gis_rc
  579. os.remove(self.gisrc_dst)
  580. self.gisrc_dst = None
  581. sht.rmtree(os.path.join(self.move, "PERMANENT"))
  582. sht.rmtree(os.path.join(self.move, self.mset.name))
  583. def patch(self):
  584. """Patch the final results."""
  585. bboxes = split_region_tiles(width=self.width, height=self.height)
  586. loc = Location()
  587. mset = loc[self.mset.name]
  588. mset.visible.extend(loc.mapsets())
  589. noutputs = 0
  590. for otmap in self.module.outputs:
  591. otm = self.module.outputs[otmap]
  592. if otm.typedesc == "raster" and otm.value:
  593. rpatch_map(
  594. otm.value,
  595. self.mset.name,
  596. self.msetstr,
  597. bboxes,
  598. self.module.flags.overwrite,
  599. self.start_row,
  600. self.start_col,
  601. self.out_prefix,
  602. )
  603. noutputs += 1
  604. if noutputs < 1:
  605. msg = "No raster output option defined for <{}>".format(self.module.name)
  606. if self.module.name == "r.mapcalc":
  607. msg += ". Use <{}.simple> instead".format(self.module.name)
  608. raise RuntimeError(msg)
  609. def rm_tiles(self):
  610. """Remove all the tiles."""
  611. # if split, remove tiles
  612. if self.inlist:
  613. grm = Module("g.remove")
  614. for key in self.inlist:
  615. grm(flags="f", type="raster", name=self.inlist[key])