|
| 1 | +import sys |
| 2 | +import shutil |
| 3 | + |
| 4 | +import grass.script as gs |
| 5 | + |
| 6 | + |
| 7 | +class ExecutedTool: |
| 8 | + def __init__(self, name, kwargs, stdout, stderr): |
| 9 | + self._name = name |
| 10 | + self._stdout = stdout |
| 11 | + |
| 12 | + @property |
| 13 | + def text(self): |
| 14 | + return self._stdout |
| 15 | + |
| 16 | + @property |
| 17 | + def json(self): |
| 18 | + import json |
| 19 | + |
| 20 | + return json.loads(self._stdout) |
| 21 | + |
| 22 | + @property |
| 23 | + def keyval(self): |
| 24 | + return gs.parse_key_val(self._stdout) |
| 25 | + |
| 26 | + |
| 27 | +class SubExecutor: |
| 28 | + """use as tools().params(a="x", b="y").g_region()""" |
| 29 | + |
| 30 | + # Can support other envs or all PIPE and encoding read command supports |
| 31 | + |
| 32 | + |
| 33 | +class Tools: |
| 34 | + def __init__(self): |
| 35 | + # TODO: fix region, so that external g.region call in the middle |
| 36 | + # is not a problem |
| 37 | + # i.e. region is independent/internal/fixed |
| 38 | + pass |
| 39 | + |
| 40 | + def run(self, name, /, **kwargs): |
| 41 | + """Run modules from the GRASS display family (modules starting with "d."). |
| 42 | +
|
| 43 | + This function passes arguments directly to grass.script.run_command() |
| 44 | + so the syntax is the same. |
| 45 | +
|
| 46 | + :param str module: name of GRASS module |
| 47 | + :param `**kwargs`: named arguments passed to run_command()""" |
| 48 | + # alternatively use dev null as default or provide it as convenient settings |
| 49 | + kwargs["stdout"] = gs.PIPE |
| 50 | + kwargs["stderr"] = gs.PIPE |
| 51 | + process = gs.pipe_command(name, **kwargs) |
| 52 | + stdout, stderr = process.communicate() |
| 53 | + stderr = gs.utils.decode(stderr) |
| 54 | + returncode = process.poll() |
| 55 | + # TODO: instead of printing, do exception right away |
| 56 | + if returncode: |
| 57 | + # Print only when we are capturing it and there was some output. |
| 58 | + # (User can request ignoring the subprocess stderr and then |
| 59 | + # we get only None.) |
| 60 | + if stderr: |
| 61 | + sys.stderr.write(stderr) |
| 62 | + gs.handle_errors(returncode, stdout, [name], kwargs) |
| 63 | + return ExecutedTool(name=name, kwargs=kwargs, stdout=stdout, stderr=stderr) |
| 64 | + |
| 65 | + def __getattr__(self, name): |
| 66 | + """Parse attribute to GRASS display module. Attribute should be in |
| 67 | + the form 'd_module_name'. For example, 'd.rast' is called with 'd_rast'. |
| 68 | + """ |
| 69 | + # Reformat string |
| 70 | + grass_module = name.replace("_", ".") |
| 71 | + # Assert module exists |
| 72 | + if not shutil.which(grass_module): |
| 73 | + raise AttributeError( |
| 74 | + _( |
| 75 | + "Cannot find GRASS tool {}. " |
| 76 | + "Is the session set up and the tool on path?" |
| 77 | + ).format(grass_module) |
| 78 | + ) |
| 79 | + |
| 80 | + def wrapper(**kwargs): |
| 81 | + # Run module |
| 82 | + return self.run(grass_module, **kwargs) |
| 83 | + |
| 84 | + return wrapper |
| 85 | + |
| 86 | + |
| 87 | +def _test(): |
| 88 | + gs.setup.init("~/grassdata/nc_spm_08_grass7/user1") |
| 89 | + |
| 90 | + tools = Tools() |
| 91 | + tools.g_region(raster="elevation") |
| 92 | + tools.r_slope_aspect(elevation="elevation", slope="slope", overwrite=True) |
| 93 | + print(tools.r_univar(map="slope", flags="g").keyval) |
| 94 | + |
| 95 | + print(tools.v_info(map="bridges", flags="c").text) |
| 96 | + print( |
| 97 | + tools.v_db_univar(map="bridges", column="YEAR_BUILT", format="json").json[ |
| 98 | + "statistics" |
| 99 | + ]["mean"] |
| 100 | + ) |
| 101 | + |
| 102 | + |
| 103 | +if __name__ == "__main__": |
| 104 | + _test() |
0 commit comments