From 9aa04e7c6dc78383e0a9ba4e93ec1080eb1566a7 Mon Sep 17 00:00:00 2001 From: ninsbl Date: Sun, 22 Sep 2024 01:26:01 +0200 Subject: [PATCH 1/8] manual --- .../r.buildvrt.gdal/r.buildvrt.gdal.html | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html diff --git a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html new file mode 100644 index 0000000000..3fe249540c --- /dev/null +++ b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html @@ -0,0 +1,68 @@ +

DESCRIPTION

+ +r.buildvrt.gdal builds GDAL virtual rasters over GRASS GIS raster +maps and links them to the mapset with r.external. The module is +written as a workaround for a limitation in GRASS GIS Virtual Rasters (VRT) +format with GDAL-linked raster maps (through r.external / +r.external.out. In that case GRASS GIS Virtual Rasters currently +show performance issues. See: +#4345 + +

+For the resulting maps GDAL VRT text files are created either in a +directory named "gdal" in the current mapset og in a userdefined +vrt_directory. Those files are not removed when the raster map is +removed and the user is responsible for removing them when needed. + +

REQUIREMENTS

+r.buildvrt.gdal uses the Python bindings for +GDAL and requires the +GDAL-GRASS driver to include raster maps in native GRASS format in +GDAL VRTs. + +

EXAMPLES

+
+# Create external example data
+regs='s,0,1000
+n,500,1500'
+
+eval `g.gisenv`
+external_path="${GISDBASE}/${LOCATION}/${MAPSET}/.tmp/vrt"
+mkdir -p "$external_path"
+for reg in $regs
+do
+  r=$(echo $reg | cut -f1 -d",")
+  s=$(echo $reg | cut -f2 -d",")
+  n=$(echo $reg | cut -f3 -d",")
+
+  g.region -g n=$n s=$s w=0 e=1000 res=1
+  r.external.out format=GTiff options="compress=LZW,PREDICTOR=3" \
+    directory="$external_path"
+  r.mapcalc --o --v expression="${r}_${s}_gtiff_ntfs=float(x()*y())"
+done
+
+# Run performance tests
+g.region -g n=1500 s=0 w=0 e=1000 res=1
+format_type=gtiff_ntfs
+rmaps=$(g.list type=raster pattern="*_*_${format_type}", sep=",")
+
+# Using GRASS GIS VRT
+r.buildvrt --o --v input="$rmaps" output=vrt_${format_type}
+time r.univar map=vrt_${format_type}
+
+# Using GDAL VRT
+r.buildvrt.gdal --o --v input="$rmaps" output=vrt_${format_type}_gdal
+time r.univar map=vrt_${format_type}_gdal
+
+
+ +

SEE ALSO

+ +r.buildvrt, +r.patch, +r.external, +r.external.out, + + +

AUTHORS

+Stefan Blumentrath From f5582b0bc01ddd154d3562011b60eb58321b65e0 Mon Sep 17 00:00:00 2001 From: ninsbl Date: Sun, 22 Sep 2024 01:27:10 +0200 Subject: [PATCH 2/8] new module r.buildvrt.gdal --- src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py diff --git a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py new file mode 100644 index 0000000000..3764dff246 --- /dev/null +++ b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 + +""" + MODULE: r.buildvrt.gdal + AUTHOR(S): Stefan Blumentrath + PURPOSE: Build GDAL Virtual Rasters (VRT) over GRASS GIS raster maps + COPYRIGHT: (C) 2024 by stefan.blumentrath, and the GRASS Development Team + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +""" + +# %module +# % description: Build GDAL Virtual Rasters (VRT) over GRASS GIS raster maps +# % keyword: raster +# % keyword: virtual +# % keyword: gdal +# % keyword: patch +# %end + +# %option G_OPT_R_INPUTS +# % key: input +# % type: string +# % required: no +# % multiple: yes +# %end + +# %option G_OPT_F_INPUT +# % key: file +# % required: no +# %end + +# %option G_OPT_M_DIR +# % key: vrt_directory +# % description: Directory to store GDAL VRT files in. Default is: $GISDBASE/$PROJECT/$MAPSET/gdal +# % required: no +# %end + +# %option G_OPT_R_OUTPUT +# %end + +# %flag +# % key: m +# % label: Read data range from metadata +# % description: WARNING: metadata are sometimes approximations with wrong data range +# %end + +# %flag +# % key: r +# % label: Create fast link without data range +# % description: WARNING: some modules do not work correctly without known data range +# %end + +# %rules +# % required: input,file +# %end + + +import sys + +from pathlib import Path + +import grass.script as gs + + +def get_raster_gdalpath(map_name, check_linked=True, gis_env=None): + """Get get the path to a raster map that can be opened by GDAL + + Checks for GDAL source of linked raster data and returns those + if not otherwise requested or the path to the header of + raster maps in native GRASS GIS format""" + if check_linked: + # Check GDAL link header + map_info = gs.find_file(map_name) + header_path = ( + Path(gis_env["GISDBASE"]) + / gis_env["LOCATION_NAME"] + / map_info["mapset"] + / "cell_misc" + / map_info["name"] + / "gdal" + ) + if header_path.exists(): + gdal_path = Path( + gs.parse_key_val(header_path.read_text().replace(": ", "="))["file"] + ) + if gdal_path.exists(): + return str(gdal_path) + + # Check if Path can be read from command history + gdal_path = Path( + gs.parse_key_val( + gs.parse_command("r.info", flags="e", map=map_name)["comments"] + .replace("\\", "") + .replace('"', ""), + vsep=" ", + )["input"] + ) + if gdal_path.exists(): + return str(gdal_path) + + # Get native GRASS GIS format header + gdal_path = Path(gs.find_file(map_name)["file"].replace("/cell/", "/cellhd/")) + if gdal_path.exists(): + return gdal_path + + # Fail if file path cannot be determined + gs.fatal(_("Cannot determine GDAL readable path to raster map {}").format(map_name)) + + +def main(): + """run the main workflow""" + + # Check if GRASS GIS driver is available + if not gdal.GetDriverByName("GRASS"): + gs.warning( + _( + "The GRASS GIS driver missing in GDAL." + "Creating VRTs for maps in native GRASS GIS format will fail. " + "Please install the GDAL-GRASS plugin." + ) + ) + + # Get GRASS GIS environment info + gisenv = gs.gisenv() + + # Get inputs + if options["input"]: + inputs = options["input"].split(",") + else: + inputs = Path(options["file"]).read_text(encoding="UTF8").strip().split("\n") + + if len(inputs) < 1: + gs.fatal(_("At least one input map is required".format(inputs[0]))) + + inputs = [get_raster_gdalpath(raster_map, gis_env=gisenv) for raster_map in inputs] + + # Get output + output = options["output"] + + # Create a directory to place GDAL VRTs in + if options["vrt_directory"]: + vrt_dir = Path(options["vrt_directory"]) + else: + vrt_dir = Path(gisenv["GISDBASE"]).joinpath( + gisenv["LOCATION_NAME"], gisenv["MAPSET"], "gdal" + ) + vrt_dir.mkdir(exist_ok=True, parents=True) + + # Create GDAL VRT + vrt_path = str(vrt_dir / f"{output}.vrt") + gs.verbose(_("Creating GDAL VRT '{}'.").format(vrt_path)) + gdal.BuildVRT(vrt_path, inputs) + + # Import (link) GDAL VRT + gs.run_command( + "r.external", + quiet=True, + overwrite=gs.overwrite(), + flags=f"oa{''.join([key for key, val in flags.items() if val])}", + input=str(vrt_path), + output=output, + ) + gs.raster_history(output, overwrite=True) + + +if __name__ == "__main__": + options, flags = gs.parser() + + # lazy imports + global gdal + try: + from osgeo import gdal + except ImportError: + gs.fatal( + _( + "Unable to load GDAL Python bindings (requires " + "package 'python-gdal' or Python library GDAL " + "to be installed)." + ) + ) + + sys.exit(main()) From b90095b61ba0a655ce9e24b54c3937e818b70600 Mon Sep 17 00:00:00 2001 From: ninsbl Date: Sun, 22 Sep 2024 01:27:43 +0200 Subject: [PATCH 3/8] Makefile --- src/raster/r.buildvrt.gdal/Makefile | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/raster/r.buildvrt.gdal/Makefile diff --git a/src/raster/r.buildvrt.gdal/Makefile b/src/raster/r.buildvrt.gdal/Makefile new file mode 100644 index 0000000000..3327d0a22e --- /dev/null +++ b/src/raster/r.buildvrt.gdal/Makefile @@ -0,0 +1,7 @@ +MODULE_TOPDIR = ../.. + +PGM=r.buildvrt.gdal + +include $(MODULE_TOPDIR)/include/Make/Script.make + +default: script From 8dbdcd80eb32a24e39ef0b66807e1ec32977a506 Mon Sep 17 00:00:00 2001 From: ninsbl Date: Sun, 22 Sep 2024 01:28:19 +0200 Subject: [PATCH 4/8] add tests --- .../testsuite/test_r_buildvrt_gdal.py | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py diff --git a/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py b/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py new file mode 100644 index 0000000000..958e7bb308 --- /dev/null +++ b/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +""" +MODULE: Test of r.buildvrt.gdal + +AUTHOR(S): Stefan Blumentrath + +PURPOSE: Test of r.buildvrt.gdal + +COPYRIGHT: (C) 2024 by Stefan Blumentrath and the GRASS Development Team + +This program is free software under the GNU General Public +License (>=v2). Read the file COPYING that comes with GRASS +for details. +""" + +import os + +from pathlib import Path + +import grass.script as gs + +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestBuildGDALVRT(TestCase): + """The main test case for the r.buildvrt.gdal module""" + + @classmethod + def setUpClass(cls): + """Ensures expected computational region (and anything else needed) + + These are things needed by all test function but not modified by + any of them. + """ + cls.vrt_univar = """n=1500000 + null_cells=0 + cells=1500000 + min=0.25 + max=1498750.25 + range=1498750 + mean=375000 + mean_of_abs=375000 + stddev=330718.777396167 + variance=109374909722.416 + coeff_var=88.1916739723113 + sum=562500000000""" + + # Create external example data + regions = {"s": (0, 1000), "n": (500, 1500)} + tmp_dir = Path(gs.tempfile(create=False)) + tmp_dir.mkdir(parents=True, exist_ok=True) + gs.run_command( + "r.external.out", + format="GTiff", + options="compress=LZW,PREDICTOR=3", + directory=str(tmp_dir), + ) + for name, ns_extent in regions.items(): + gs.run_command( + "g.region", n=ns_extent[1], s=ns_extent[0], w=0, e=1000, res=1 + ) + gs.mapcalc(f"tmp_vrt_gtiff_{ns_extent[1]}_{ns_extent[0]}=float(x()*y())") + # Set region + gs.use_temp_region() + gs.run_command("g.region", n=1500, s=0, w=0, e=1000, res=1) + + @classmethod + def tearDownClass(cls): + """Remove the temporary region (and anything else we created)""" + gs.del_temp_region() + gs.run_command("g.remove", flags="f", type="raster", pattern="tmp_vrt_g*_*") + + def tearDown(self): + """Remove the output created from the module + + This is executed after each test function run. If we had + something to set up before each test function run, we would use setUp() + function. + + Since we remove the raster map after running each test function, + we can reuse the same name for all the test functions. + """ + gs.run_command("g.remove", flags="f", type="raster", pattern="tmp_vrt_gda*") + + def test_r_buildvrt_gdal_no_flag(self): + """Check that the output is created and readable""" + # run the import module + raster_maps = gs.list_strings(type="raster", pattern="tmp_vrt_gtiff*_*") + + self.assertModule( + "r.buildvrt.gdal", + verbose=True, + input=",".join(raster_maps), + output="tmp_vrt_gdal", + ) + vrt_info = """north=1500 +south=0 +east=1000 +west=0 +nsres=1 +ewres=1 +rows=1500 +cols=1000 +cells=1500000 +datatype=FCELL +ncats=0 +min=0.25 +max=1498750 +map=tmp_vrt_gdal +maptype=GDAL-link +title="" +timestamp="none" +units="none" +vdatum="none" +semantic_label="none" +""" + self.assertRasterFitsUnivar( + "tmp_vrt_gdal", reference=self.vrt_univar, precision=2 + ) + self.assertRasterFitsInfo("tmp_vrt_gdal", reference=vrt_info, precision=2) + + def test_r_buildvrt_gdal_r_flag(self): + """Check that the output is created and readable with r-flag""" + # run the import module + raster_maps = gs.list_strings(type="raster", pattern="tmp_vrt_gtiff*_*") + + self.assertModule( + "r.buildvrt.gdal", + flags="r", + verbose=True, + input=",".join(raster_maps), + output="tmp_vrt_gdal_r", + ) + self.assertRasterFitsUnivar( + "tmp_vrt_gdal_r", reference=self.vrt_univar, precision=2 + ) + + def test_r_buildvrt_gdal_vrt_dir(self): + """Check that the output is created and readable with r-flag""" + # run the import module + raster_maps = gs.list_strings(type="raster", pattern="tmp_vrt_gtiff*_*") + vrt_directory = gs.tempfile(create=False) + self.assertModule( + "r.buildvrt.gdal", + verbose=True, + input=",".join(raster_maps), + output="tmp_vrt_gdal_dir", + vrt_directory=vrt_directory, + ) + self.assertRasterFitsUnivar( + "tmp_vrt_gdal_dir", reference=self.vrt_univar, precision=2 + ) + self.assertFileExists(vrt_directory + "/tmp_vrt_gdal_dir.vrt") + + +if __name__ == "__main__": + test() From 995faf888663a2ddcb1868814c33314e163b0238 Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Sun, 22 Sep 2024 14:11:43 +0200 Subject: [PATCH 5/8] Apply suggestions from code review Co-authored-by: Vaclav Petras Co-authored-by: Markus Neteler --- src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html | 5 ++--- src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html index 3fe249540c..16c95d7ef7 100644 --- a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html +++ b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.html @@ -10,7 +10,7 @@

DESCRIPTION

For the resulting maps GDAL VRT text files are created either in a -directory named "gdal" in the current mapset og in a userdefined +directory named "gdal" in the current mapset or in a user-defined vrt_directory. Those files are not removed when the raster map is removed and the user is responsible for removing them when needed. @@ -53,7 +53,6 @@

EXAMPLES

# Using GDAL VRT r.buildvrt.gdal --o --v input="$rmaps" output=vrt_${format_type}_gdal time r.univar map=vrt_${format_type}_gdal -

SEE ALSO

@@ -61,7 +60,7 @@

SEE ALSO

r.buildvrt, r.patch, r.external, -r.external.out, +r.external.out

AUTHORS

diff --git a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py index 3764dff246..f6914b7ec0 100644 --- a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py +++ b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py @@ -90,7 +90,7 @@ def get_raster_gdalpath(map_name, check_linked=True, gis_env=None): ) if header_path.exists(): gdal_path = Path( - gs.parse_key_val(header_path.read_text().replace(": ", "="))["file"] + gs.parse_key_val(header_path.read_text(), sep=": "))["file"] ) if gdal_path.exists(): return str(gdal_path) From 0a519104a4f1642837c320e7c689967ec3fc2293 Mon Sep 17 00:00:00 2001 From: ninsbl Date: Sun, 22 Sep 2024 23:29:02 +0200 Subject: [PATCH 6/8] add more tests --- .../testsuite/test_r_buildvrt_gdal.py | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py b/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py index 958e7bb308..4e5d0adaaa 100644 --- a/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py +++ b/src/raster/r.buildvrt.gdal/testsuite/test_r_buildvrt_gdal.py @@ -51,17 +51,24 @@ def setUpClass(cls): regions = {"s": (0, 1000), "n": (500, 1500)} tmp_dir = Path(gs.tempfile(create=False)) tmp_dir.mkdir(parents=True, exist_ok=True) + cls.map_input_file = tmp_dir / "map_file.txt" gs.run_command( "r.external.out", format="GTiff", options="compress=LZW,PREDICTOR=3", directory=str(tmp_dir), ) + map_list = [] for name, ns_extent in regions.items(): gs.run_command( "g.region", n=ns_extent[1], s=ns_extent[0], w=0, e=1000, res=1 ) - gs.mapcalc(f"tmp_vrt_gtiff_{ns_extent[1]}_{ns_extent[0]}=float(x()*y())") + map_name = f"tmp_vrt_gtiff_{ns_extent[1]}_{ns_extent[0]}" + map_list.append(map_name) + gs.mapcalc(f"{map_name}=float(x()*y())") + + cls.map_input_file.write_text("\n".join(map_list), encoding="UTF8") + # Set region gs.use_temp_region() gs.run_command("g.region", n=1500, s=0, w=0, e=1000, res=1) @@ -154,6 +161,42 @@ def test_r_buildvrt_gdal_vrt_dir(self): ) self.assertFileExists(vrt_directory + "/tmp_vrt_gdal_dir.vrt") + def test_r_buildvrt_fails_rm(self): + """Check that module fails with both -m and -r""" + raster_maps = gs.list_strings(type="raster", pattern="tmp_vrt_gtiff*_*") + # run the import module + self.assertModuleFail( + "r.buildvrt.gdal", + verbose=True, + input=",".join(raster_maps), + file=str(self.map_input_file), + output="tmp_vrt_gdal_input_file", + ) + + def test_r_buildvrt_fails_rm(self): + """Check that module fails with both -m and -r""" + # run the import module + self.assertModuleFail( + "r.buildvrt.gdal", + verbose=True, + flags="rm", + file=str(self.map_input_file), + output="tmp_vrt_gdal_rm", + ) + + def test_r_buildvrt_gdal_vrt_file(self): + """Check that the output is created and readable with file input""" + # run the import module + self.assertModule( + "r.buildvrt.gdal", + verbose=True, + file=str(self.map_input_file), + output="tmp_vrt_gdal_file", + ) + self.assertRasterFitsUnivar( + "tmp_vrt_gdal_file", reference=self.vrt_univar, precision=2 + ) + if __name__ == "__main__": test() From bd4e2061a73f6acf4d5f314abdb7cd7112da8088 Mon Sep 17 00:00:00 2001 From: ninsbl Date: Sun, 22 Sep 2024 23:31:09 +0200 Subject: [PATCH 7/8] address code review, add rules --- src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py | 80 ++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py index f6914b7ec0..4ad5e0d456 100644 --- a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py +++ b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py @@ -61,9 +61,12 @@ # %rules # % required: input,file +# % exclusive: input,file +# % exclusive: -m,-r # %end +import json import sys from pathlib import Path @@ -71,7 +74,9 @@ import grass.script as gs -def get_raster_gdalpath(map_name, check_linked=True, gis_env=None): +def get_raster_gdalpath( + map_name, check_linked=True, has_grasadriver=False, gis_env=None +): """Get get the path to a raster map that can be opened by GDAL Checks for GDAL source of linked raster data and returns those @@ -88,28 +93,37 @@ def get_raster_gdalpath(map_name, check_linked=True, gis_env=None): / map_info["name"] / "gdal" ) - if header_path.exists(): + if header_path.is_file(): gdal_path = Path( - gs.parse_key_val(header_path.read_text(), sep=": "))["file"] + gs.parse_key_val(header_path.read_text(), sep=": ")["file"] ) if gdal_path.exists(): return str(gdal_path) - # Check if Path can be read from command history - gdal_path = Path( - gs.parse_key_val( - gs.parse_command("r.info", flags="e", map=map_name)["comments"] - .replace("\\", "") - .replace('"', ""), - vsep=" ", - )["input"] - ) - if gdal_path.exists(): - return str(gdal_path) + # Check if path to GDAL-readable input dataset can be read + # from command history; normaly this should not be reached + raster_history = json.loads( + gs.read_command("r.info", format="json", map=map_name) + )["comments"].split("\n") + for comment in raster_history: + if "input" not in comment: + continue + gdal_path = Path(gs.parse_key_val(comment, vsep=" ")["input"]) + if gdal_path.is_file(): + return str(gdal_path) # Get native GRASS GIS format header + if not has_grasadriver: + gs.fatal( + _( + "The GDAL-GRASS GIS driver is unavailable. " + "Cannot create GDAL VRTs for map <{}>. " + "Please install the GDAL-GRASS plugin." + ).format(map_name) + ) + gdal_path = Path(gs.find_file(map_name)["file"].replace("/cell/", "/cellhd/")) - if gdal_path.exists(): + if gdal_path.is_file(): return gdal_path # Fail if file path cannot be determined @@ -118,17 +132,26 @@ def get_raster_gdalpath(map_name, check_linked=True, gis_env=None): def main(): """run the main workflow""" + options, flags = gs.parser() - # Check if GRASS GIS driver is available - if not gdal.GetDriverByName("GRASS"): - gs.warning( + # lazy imports + global gdal + try: + from osgeo import gdal + except ImportError: + gs.fatal( _( - "The GRASS GIS driver missing in GDAL." - "Creating VRTs for maps in native GRASS GIS format will fail. " - "Please install the GDAL-GRASS plugin." + "Unable to load GDAL Python bindings (requires " + "package 'python-gdal' or Python library GDAL " + "to be installed)." ) ) + # Check if GRASS GIS driver is available + has_grassdriver = True + if not gdal.GetDriverByName("GRASS"): + has_grassdriver = False + # Get GRASS GIS environment info gisenv = gs.gisenv() @@ -164,7 +187,6 @@ def main(): gs.run_command( "r.external", quiet=True, - overwrite=gs.overwrite(), flags=f"oa{''.join([key for key, val in flags.items() if val])}", input=str(vrt_path), output=output, @@ -173,19 +195,5 @@ def main(): if __name__ == "__main__": - options, flags = gs.parser() - - # lazy imports - global gdal - try: - from osgeo import gdal - except ImportError: - gs.fatal( - _( - "Unable to load GDAL Python bindings (requires " - "package 'python-gdal' or Python library GDAL " - "to be installed)." - ) - ) sys.exit(main()) From ea2a29ddcde6073d00dfa0817852e44733ab8a3e Mon Sep 17 00:00:00 2001 From: ninsbl Date: Mon, 23 Sep 2024 23:15:03 +0200 Subject: [PATCH 8/8] remove unstable way to get GDAL-path --- src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py index 4ad5e0d456..a9ab63c0b5 100644 --- a/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py +++ b/src/raster/r.buildvrt.gdal/r.buildvrt.gdal.py @@ -77,11 +77,12 @@ def get_raster_gdalpath( map_name, check_linked=True, has_grasadriver=False, gis_env=None ): - """Get get the path to a raster map that can be opened by GDAL + """Get the GDAL-readable path to a GRASS GIS raster map - Checks for GDAL source of linked raster data and returns those - if not otherwise requested or the path to the header of - raster maps in native GRASS GIS format""" + Returns either the link stored in the GDAL-link file in the cell_misc + directory for raster maps linked with r.external or r.external.out + - if requested - or the path to the header of the GRASS GIS raster + map""" if check_linked: # Check GDAL link header map_info = gs.find_file(map_name) @@ -100,18 +101,6 @@ def get_raster_gdalpath( if gdal_path.exists(): return str(gdal_path) - # Check if path to GDAL-readable input dataset can be read - # from command history; normaly this should not be reached - raster_history = json.loads( - gs.read_command("r.info", format="json", map=map_name) - )["comments"].split("\n") - for comment in raster_history: - if "input" not in comment: - continue - gdal_path = Path(gs.parse_key_val(comment, vsep=" ")["input"]) - if gdal_path.is_file(): - return str(gdal_path) - # Get native GRASS GIS format header if not has_grasadriver: gs.fatal(