From 1e4fd40db7f2af4febc28b077faa980ecf76b875 Mon Sep 17 00:00:00 2001 From: Salih Tangel Date: Sat, 14 Mar 2026 18:03:30 +0300 Subject: [PATCH 1/6] add subcommand for --link-raster --- python/grass/app/cli.py | 79 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/python/grass/app/cli.py b/python/grass/app/cli.py index 0cf05afbb47..d100f6b01a3 100644 --- a/python/grass/app/cli.py +++ b/python/grass/app/cli.py @@ -68,6 +68,42 @@ def subcommand_run_tool(args, tool_args: list, print_help: bool) -> int: # other special flags, so later use of --json in tools will fail # with the other flags active. return tools.call_cmd(command).returncode + if args.link_raster: + for raster in args.link_raster: + gs.run_command( + "r.external", + input=raster, + output=Path(raster).stem, + flags="o", + env=session.env, + errors="status", + ) + if args.link_vector: + for vector in args.link_vector: + gs.run_command( + "v.external", + input=vector, + output=Path(vector).stem, + flags="o", + env=session.env, + errors="status", + ) + if args.out_raster: + for out_r in args.out_raster: + gs.run_command( + "r.external.out", + directory=out_r, + env=session.env, + errors="status", + ) + if args.out_vector: + for out_v in args.out_vector: + gs.run_command( + "v.external.out", + output=out_v, + env=session.env, + errors="status", + ) return tools.run_cmd(command).returncode except subprocess.CalledProcessError as error: return error.returncode @@ -94,7 +130,7 @@ def subcommand_create_project(args) -> int: def add_mapset_subparser(subparsers): mapset_subparser = subparsers.add_parser("mapset", help="mapset related operations") - mapset_subparsers = mapset_subparser.add_subparsers(dest="mapset_command") + mapset_subparsers = mapset_subparser.add_subparsers(dest="mapset_comm§and") subparser = mapset_subparsers.add_parser("create", help="create a new mapset") subparser.add_argument("path", help="path to the new mapset") @@ -224,6 +260,26 @@ def add_project_subparser(subparsers): action="store_true", help="overwrite existing project", ) + create_parser.add_argument( + "--link-raster", + action="append", + help="link a raster map to the project (can be used multiple times)", + ) + create_parser.add_argument( + "--link-vector", + action="append", + help="link a vector map to the project (can be used multiple times)", + ) + create_parser.add_argument( + "--out-raster", + action="append", + help="define external format for raster output (can be used multiple times)", + ) + create_parser.add_argument( + "--out-vector", + action="append", + help="define external format for vector output (can be used multiple times)", + ) create_parser.set_defaults(func=subcommand_create_project) @@ -255,8 +311,27 @@ def main(args=None, program=None): run_subparser.add_argument( "--project", type=str, help="project to use for computations" ) + run_subparser.add_argument("--link-raster", type=str, nargs="+",help="Link a raster file " + "(r.external) before execution") + run_subparser.add_argument( + "--link-vector", + type=str, + nargs="+", + help="Link a vector file (v.external) before execution" + ) + run_subparser.add_argument( + "--out-raster", + type=str, + nargs="+", + help="Define external format for raster output (r.external.out)" + ) + run_subparser.add_argument( + "--out-vector", + type=str, + nargs="+", + help="Define external format for vector output (v.external.out)" + ) run_subparser.set_defaults(func=subcommand_run_tool) - add_project_subparser(subparsers) add_mapset_subparser(subparsers) From 0efb3661fa806b8f8474ae824d67dca8e4aa6c1f Mon Sep 17 00:00:00 2001 From: Salih Tangel Date: Sat, 14 Mar 2026 18:31:58 +0300 Subject: [PATCH 2/6] add --link-raster --- python/grass/app/cli.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/python/grass/app/cli.py b/python/grass/app/cli.py index d100f6b01a3..db43028fc21 100644 --- a/python/grass/app/cli.py +++ b/python/grass/app/cli.py @@ -92,7 +92,7 @@ def subcommand_run_tool(args, tool_args: list, print_help: bool) -> int: for out_r in args.out_raster: gs.run_command( "r.external.out", - directory=out_r, + directory=out_r, env=session.env, errors="status", ) @@ -311,25 +311,29 @@ def main(args=None, program=None): run_subparser.add_argument( "--project", type=str, help="project to use for computations" ) - run_subparser.add_argument("--link-raster", type=str, nargs="+",help="Link a raster file " - "(r.external) before execution") run_subparser.add_argument( - "--link-vector", - type=str, + "--link-raster", + type=str, nargs="+", - help="Link a vector file (v.external) before execution" + help="Link a raster file (r.external) before execution", ) run_subparser.add_argument( - "--out-raster", - type=str, + "--link-vector", + type=str, nargs="+", - help="Define external format for raster output (r.external.out)" + help="Link a vector file (v.external) before execution", ) run_subparser.add_argument( - "--out-vector", - type=str, + "--out-raster", + type=str, + nargs="+", + help="Define external format for raster output (r.external.out)", + ) + run_subparser.add_argument( + "--out-vector", + type=str, nargs="+", - help="Define external format for vector output (v.external.out)" + help="Define external format for vector output (v.external.out)", ) run_subparser.set_defaults(func=subcommand_run_tool) add_project_subparser(subparsers) From 310a46a84def5cac14098228e9cb9d3d6ee326aa Mon Sep 17 00:00:00 2001 From: Salih Tangel Date: Sat, 14 Mar 2026 18:34:50 +0300 Subject: [PATCH 3/6] add subcommand for link raster --- python/grass/app/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/grass/app/cli.py b/python/grass/app/cli.py index db43028fc21..1a01059f1d0 100644 --- a/python/grass/app/cli.py +++ b/python/grass/app/cli.py @@ -130,7 +130,7 @@ def subcommand_create_project(args) -> int: def add_mapset_subparser(subparsers): mapset_subparser = subparsers.add_parser("mapset", help="mapset related operations") - mapset_subparsers = mapset_subparser.add_subparsers(dest="mapset_comm§and") + mapset_subparsers = mapset_subparser.add_subparsers(dest="mapset_command") subparser = mapset_subparsers.add_parser("create", help="create a new mapset") subparser.add_argument("path", help="path to the new mapset") @@ -336,6 +336,7 @@ def main(args=None, program=None): help="Define external format for vector output (v.external.out)", ) run_subparser.set_defaults(func=subcommand_run_tool) + add_project_subparser(subparsers) add_mapset_subparser(subparsers) From 8184e3353a002d1f09e5196559e2f745d7c020ea Mon Sep 17 00:00:00 2001 From: Salih Tangel Date: Tue, 17 Mar 2026 18:14:54 +0300 Subject: [PATCH 4/6] vector and raster out implemented --- python/grass/app/cli.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/grass/app/cli.py b/python/grass/app/cli.py index 1a01059f1d0..da0d828a492 100644 --- a/python/grass/app/cli.py +++ b/python/grass/app/cli.py @@ -78,6 +78,7 @@ def subcommand_run_tool(args, tool_args: list, print_help: bool) -> int: env=session.env, errors="status", ) + gs.run_command("g.list", type="raster", env=session.env) if args.link_vector: for vector in args.link_vector: gs.run_command( @@ -90,19 +91,25 @@ def subcommand_run_tool(args, tool_args: list, print_help: bool) -> int: ) if args.out_raster: for out_r in args.out_raster: + abs_out_path = os.path.abspath(out_r) + Path(abs_out_path).mkdir(exist_ok=True, parents=True) gs.run_command( "r.external.out", - directory=out_r, + directory=abs_out_path, env=session.env, errors="status", + format="GTiff", ) if args.out_vector: for out_v in args.out_vector: + abs_out_path = os.path.abspath(out_v) + Path(abs_out_path).mkdir(exist_ok=True, parents=True) gs.run_command( "v.external.out", output=out_v, env=session.env, errors="status", + format="GPKG", ) return tools.run_cmd(command).returncode except subprocess.CalledProcessError as error: From 3063be1b2123aad5f7314e0ef4ba94d230aa6e8a Mon Sep 17 00:00:00 2001 From: Salih Tangel Date: Sun, 29 Mar 2026 17:35:41 +0300 Subject: [PATCH 5/6] Code Tests are added. --- python/grass/app/cli.py | 19 ++--- .../r.external/testsuite/test_r_external.py | 77 +++++++++++++++++++ 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/python/grass/app/cli.py b/python/grass/app/cli.py index da0d828a492..65b655b101d 100644 --- a/python/grass/app/cli.py +++ b/python/grass/app/cli.py @@ -227,7 +227,7 @@ def subcommand_show_help(args): def subcommand_show_man(args): return call_g_manual(entry=args.page, flags="m") - +#dddadasd def add_project_subparser(subparsers): project_parser = subparsers.add_parser("project", help="project operations") project_subparsers = project_parser.add_subparsers(dest="project_command") @@ -303,7 +303,6 @@ def main(args=None, program=None): subparsers = parser.add_subparsers( title="subcommands", dest="subcommand", required=True ) - # Subcommand parsers run_subparser = subparsers.add_parser( @@ -319,27 +318,19 @@ def main(args=None, program=None): "--project", type=str, help="project to use for computations" ) run_subparser.add_argument( - "--link-raster", - type=str, - nargs="+", + "--link-raster", type=str,action="append", help="Link a raster file (r.external) before execution", ) run_subparser.add_argument( - "--link-vector", - type=str, - nargs="+", + "--link-vector",type=str,action="append", help="Link a vector file (v.external) before execution", ) run_subparser.add_argument( - "--out-raster", - type=str, - nargs="+", + "--out-raster",type=str,action="append", help="Define external format for raster output (r.external.out)", ) run_subparser.add_argument( - "--out-vector", - type=str, - nargs="+", + "--out-vector",type=str,action="append", help="Define external format for vector output (v.external.out)", ) run_subparser.set_defaults(func=subcommand_run_tool) diff --git a/raster/r.external/testsuite/test_r_external.py b/raster/r.external/testsuite/test_r_external.py index a22498eb773..81db192db38 100644 --- a/raster/r.external/testsuite/test_r_external.py +++ b/raster/r.external/testsuite/test_r_external.py @@ -134,6 +134,83 @@ def test_4(self): raster="test_external_map", reference=univar_string, precision=3 ) + def test_link_raster_simple(self): + self.assertModule( + "r.external", + input="data/elevation.asc", + output="test_external_map", + flags="e", + ) + + self.runModule("g.region", raster="test_external_map") + + def test_link_raster_with_title(self): + self.assertModule( + "r.external", + input="data/elevation.asc", + output="test_external_map", + title="Test Elevation Map", + flags="e", + ) + + def test_link_raster_overwrite(self): + self.runModule( + "r.external", + input="data/elevation.asc", + output="test_external_map", + flags="e", + ) + + self.assertModule( + "r.external", + input="data/elevation.asc", + output="test_external_map", + flags="e", + overwrite=True, + ) + + def test_invalid_input_fails(self): + self.assertModuleFail( + "r.external", + input="non_existent_file.tif", + output="should_not_exist", + flags="e", + ) + + def test_link_multiple_rasters(self): + self.runModule("g.remove", type="raster", name="map_1", flags="f") + self.runModule("g.remove", type="raster", name="map_2", flags="f") + + self.assertModule( + "r.external", input="data/elevation.asc", output="map_1", flags="e" + ) + + self.assertModule( + "r.external", input="data/elevation.asc", output="map_2", flags="e" + ) + + def test_link_sequential_calls(self): + self.runModule("g.remove", type="raster", name="first_map", flags="f") + self.runModule("g.remove", type="raster", name="second_map", flags="f") + + self.runModule( + "r.external", input="data/elevation.asc", output="first_map", flags="e" + ) + + self.assertModule( + "r.external", input="data/elevation.asc", output="second_map", flags="e" + ) + + def test_link_with_project_context(self): + self.assertModule( + "r.external", + input="data/elevation.asc", + output="test_external_map", + flags="e", + ) + + self.runModule("g.list", type="raster", pattern="test_external_map") + def test_netCDF_3d_1(self): self.assertModule( "r.external", From 8f0e47b3d99e4ae295cd0579e4bc21aa4fba062e Mon Sep 17 00:00:00 2001 From: Salih Tangel Date: Sun, 29 Mar 2026 18:12:07 +0300 Subject: [PATCH 6/6] test are added. --- python/grass/app/cli.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/python/grass/app/cli.py b/python/grass/app/cli.py index 65b655b101d..24f0e2356ed 100644 --- a/python/grass/app/cli.py +++ b/python/grass/app/cli.py @@ -227,7 +227,7 @@ def subcommand_show_help(args): def subcommand_show_man(args): return call_g_manual(entry=args.page, flags="m") -#dddadasd + def add_project_subparser(subparsers): project_parser = subparsers.add_parser("project", help="project operations") project_subparsers = project_parser.add_subparsers(dest="project_command") @@ -318,19 +318,27 @@ def main(args=None, program=None): "--project", type=str, help="project to use for computations" ) run_subparser.add_argument( - "--link-raster", type=str,action="append", + "--link-raster", + type=str, + action="append", help="Link a raster file (r.external) before execution", ) run_subparser.add_argument( - "--link-vector",type=str,action="append", + "--link-vector", + type=str, + action="append", help="Link a vector file (v.external) before execution", ) run_subparser.add_argument( - "--out-raster",type=str,action="append", + "--out-raster", + type=str, + action="append", help="Define external format for raster output (r.external.out)", ) run_subparser.add_argument( - "--out-vector",type=str,action="append", + "--out-vector", + type=str, + action="append", help="Define external format for vector output (v.external.out)", ) run_subparser.set_defaults(func=subcommand_run_tool)