grid.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. # -*- coding: utf-8 -*-
  2. from __future__ import (
  3. nested_scopes,
  4. generators,
  5. division,
  6. absolute_import,
  7. with_statement,
  8. print_function,
  9. unicode_literals,
  10. )
  11. import os
  12. import sys
  13. import multiprocessing as mltp
  14. import subprocess as sub
  15. import shutil as sht
  16. from grass.script.setup import write_gisrc
  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. """
  380. def __init__(
  381. self,
  382. cmd,
  383. width=None,
  384. height=None,
  385. overlap=0,
  386. processes=None,
  387. split=False,
  388. debug=False,
  389. region=None,
  390. move=None,
  391. log=False,
  392. start_row=0,
  393. start_col=0,
  394. out_prefix="",
  395. mapset_prefix=None,
  396. *args,
  397. **kargs,
  398. ):
  399. kargs["run_"] = False
  400. self.mset = Mapset()
  401. self.module = Module(cmd, *args, **kargs)
  402. self.width = width
  403. self.height = height
  404. self.overlap = overlap
  405. self.processes = processes
  406. self.region = region if region else Region()
  407. self.start_row = start_row
  408. self.start_col = start_col
  409. self.out_prefix = out_prefix
  410. self.log = log
  411. self.move = move
  412. self.gisrc_src = os.environ["GISRC"]
  413. self.n_mset, self.gisrc_dst = None, None
  414. if self.move:
  415. self.n_mset = copy_mapset(self.mset, self.move)
  416. self.gisrc_dst = write_gisrc(
  417. self.n_mset.gisdbase, self.n_mset.location, self.n_mset.name
  418. )
  419. rasters = [r for r in select(self.module.inputs, "raster")]
  420. if rasters:
  421. copy_rasters(
  422. rasters, self.gisrc_src, self.gisrc_dst, region=self.region
  423. )
  424. vectors = [v for v in select(self.module.inputs, "vector")]
  425. if vectors:
  426. copy_vectors(vectors, self.gisrc_src, self.gisrc_dst)
  427. groups = [g for g in select(self.module.inputs, "group")]
  428. if groups:
  429. copy_groups(groups, self.gisrc_src, self.gisrc_dst, region=self.region)
  430. self.bboxes = split_region_tiles(
  431. region=region, width=width, height=height, overlap=overlap
  432. )
  433. if mapset_prefix:
  434. self.msetstr = mapset_prefix + "_%03d_%03d"
  435. else:
  436. self.msetstr = cmd.replace(".", "") + "_%03d_%03d"
  437. self.inlist = None
  438. if split:
  439. self.split()
  440. self.debug = debug
  441. def __del__(self):
  442. if self.gisrc_dst:
  443. # remove GISRC file
  444. os.remove(self.gisrc_dst)
  445. def clean_location(self, location=None):
  446. """Remove all created mapsets.
  447. :param location: a Location instance where we are running the analysis
  448. :type location: Location object
  449. """
  450. if location is None:
  451. if self.n_mset:
  452. self.n_mset.current()
  453. location = Location()
  454. mapsets = location.mapsets(self.msetstr.split("_")[0] + "_*")
  455. for mset in mapsets:
  456. Mapset(mset).delete()
  457. if self.n_mset and self.n_mset.is_current():
  458. self.mset.current()
  459. def split(self):
  460. """Split all the raster inputs using r.tile"""
  461. rtile = Module("r.tile")
  462. inlist = {}
  463. for inm in select(self.module.inputs, "raster"):
  464. rtile(
  465. input=inm.value,
  466. output=inm.value,
  467. width=self.width,
  468. height=self.height,
  469. overlap=self.overlap,
  470. )
  471. patt = "%s-*" % inm.value
  472. inlist[inm.value] = sorted(self.mset.glist(type="raster", pattern=patt))
  473. self.inlist = inlist
  474. def get_works(self):
  475. """Return a list of tuble with the parameters for cmd_exe function"""
  476. works = []
  477. reg = Region()
  478. if self.move:
  479. mdst, ldst, gdst = read_gisrc(self.gisrc_dst)
  480. else:
  481. ldst, gdst = self.mset.location, self.mset.gisdbase
  482. cmd = self.module.get_dict()
  483. groups = [g for g in select(self.module.inputs, "group")]
  484. for row, box_row in enumerate(self.bboxes):
  485. for col, box in enumerate(box_row):
  486. inms = None
  487. if self.inlist:
  488. inms = {}
  489. cols = len(box_row)
  490. for key in self.inlist:
  491. indx = row * cols + col
  492. inms[key] = "%s@%s" % (self.inlist[key][indx], self.mset.name)
  493. # set the computational region, prepare the region parameters
  494. bbox = dict([(k[0], str(v)) for k, v in box.items()[:-2]])
  495. bbox["nsres"] = "%f" % reg.nsres
  496. bbox["ewres"] = "%f" % reg.ewres
  497. new_mset = (
  498. self.msetstr % (self.start_row + row, self.start_col + col),
  499. )
  500. works.append(
  501. (
  502. bbox,
  503. inms,
  504. self.gisrc_src,
  505. write_gisrc(gdst, ldst, new_mset),
  506. cmd,
  507. groups,
  508. )
  509. )
  510. return works
  511. def define_mapset_inputs(self):
  512. """Add the mapset information to the input maps"""
  513. for inmap in self.module.inputs:
  514. inm = self.module.inputs[inmap]
  515. if inm.type in ("raster", "vector") and inm.value:
  516. if "@" not in inm.value:
  517. mset = get_mapset_raster(inm.value)
  518. inm.value = inm.value + "@%s" % mset
  519. def run(self, patch=True, clean=True):
  520. """Run the GRASS command
  521. :param patch: set False if you does not want to patch the results
  522. :type patch: bool
  523. :param clean: set False if you does not want to remove all the stuff
  524. created by GridModule
  525. :type clean: bool
  526. """
  527. self.module.flags.overwrite = True
  528. self.define_mapset_inputs()
  529. if self.debug:
  530. for wrk in self.get_works():
  531. cmd_exe(wrk)
  532. else:
  533. pool = mltp.Pool(processes=self.processes)
  534. result = pool.map_async(cmd_exe, self.get_works())
  535. result.wait()
  536. pool.close()
  537. pool.join()
  538. if not result.successful():
  539. raise RuntimeError(_("Execution of subprocesses was not successful"))
  540. if patch:
  541. if self.move:
  542. os.environ["GISRC"] = self.gisrc_dst
  543. self.n_mset.current()
  544. self.patch()
  545. os.environ["GISRC"] = self.gisrc_src
  546. self.mset.current()
  547. # copy the outputs from dst => src
  548. routputs = [
  549. self.out_prefix + o for o in select(self.module.outputs, "raster")
  550. ]
  551. copy_rasters(routputs, self.gisrc_dst, self.gisrc_src)
  552. else:
  553. self.patch()
  554. if self.log:
  555. # record in the temp directory
  556. from grass.lib.gis import G_tempfile
  557. tmp, dummy = os.path.split(G_tempfile())
  558. tmpdir = os.path.join(tmp, self.module.name)
  559. for k in self.module.outputs:
  560. par = self.module.outputs[k]
  561. if par.typedesc == "raster" and par.value:
  562. dirpath = os.path.join(tmpdir, par.name)
  563. if not os.path.isdir(dirpath):
  564. os.makedirs(dirpath)
  565. fil = open(os.path.join(dirpath, self.out_prefix + par.value), "w+")
  566. fil.close()
  567. if clean:
  568. self.clean_location()
  569. self.rm_tiles()
  570. if self.n_mset:
  571. gisdbase, location = os.path.split(self.move)
  572. self.clean_location(Location(location, gisdbase))
  573. # rm temporary gis_rc
  574. os.remove(self.gisrc_dst)
  575. self.gisrc_dst = None
  576. sht.rmtree(os.path.join(self.move, "PERMANENT"))
  577. sht.rmtree(os.path.join(self.move, self.mset.name))
  578. def patch(self):
  579. """Patch the final results."""
  580. bboxes = split_region_tiles(width=self.width, height=self.height)
  581. loc = Location()
  582. mset = loc[self.mset.name]
  583. mset.visible.extend(loc.mapsets())
  584. noutputs = 0
  585. for otmap in self.module.outputs:
  586. otm = self.module.outputs[otmap]
  587. if otm.typedesc == "raster" and otm.value:
  588. rpatch_map(
  589. otm.value,
  590. self.mset.name,
  591. self.msetstr,
  592. bboxes,
  593. self.module.flags.overwrite,
  594. self.start_row,
  595. self.start_col,
  596. self.out_prefix,
  597. )
  598. noutputs += 1
  599. if noutputs < 1:
  600. msg = "No raster output option defined for <{}>".format(self.module.name)
  601. if self.module.name == "r.mapcalc":
  602. msg += ". Use <{}.simple> instead".format(self.module.name)
  603. raise RuntimeError(msg)
  604. def rm_tiles(self):
  605. """Remove all the tiles."""
  606. # if split, remove tiles
  607. if self.inlist:
  608. grm = Module("g.remove")
  609. for key in self.inlist:
  610. grm(flags="f", type="raster", name=self.inlist[key])