From ce5f834a671261d0825ac8e6bb3599ff85ae9482 Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Mon, 24 Oct 2022 21:06:22 +0200 Subject: [PATCH] t.rast.univar / t.rast3d.univar: Add support for zones (#2588) * add support for zones * add support for zones * add test for zones * clean properly * add test for zones * add support for zones * add credits * add credits * add zones in manual * add check for zones map * black * use RasterRow context manager * fix zones existence test * try if centos fails because of context manager * fix indent * change import order * zones check for t.rast3d.univar * avoid RasterRow * avoid RasterRow * fix if * fix if * fix raster_info * fix raster_info * fix raster_info * fix raster_info * import as gs * import as gs and zones * string formating * clean test * add semantic labels * black * remove gscript * name tests * name tests * move parser, use kwargs * move parser, use kwargs, allow DCELL * Import tgis after parser * Import tgis after parser * Fix typo --- python/grass/temporal/univar_statistics.py | 122 ++++++++---- temporal/t.rast.univar/t.rast.univar.html | 9 +- temporal/t.rast.univar/t.rast.univar.py | 33 +++- .../testsuite/test_t_rast_univar.py | 177 +++++++++++++++--- temporal/t.rast3d.univar/t.rast3d.univar.html | 1 + temporal/t.rast3d.univar/t.rast3d.univar.py | 29 ++- .../testsuite/test_t_rast3d_univar.py | 56 +++++- 7 files changed, 349 insertions(+), 78 deletions(-) diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index 04ff3022210..bcb6bc6abe4 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -29,7 +29,15 @@ def print_gridded_dataset_univar_statistics( - type, input, output, where, extended, no_header=False, fs="|", rast_region=False + type, + input, + output, + where, + extended, + no_header=False, + fs="|", + rast_region=False, + zones=None, ): """Print univariate statistics for a space time raster or raster3d dataset @@ -43,8 +51,14 @@ def print_gridded_dataset_univar_statistics( :param rast_region: If set True ignore the current region settings and use the raster map regions for univar statistical calculation. Only available for strds. + :param zones: raster map with zones to calculate statistics for """ + stats_module = { + "strds": "r.univar", + "str3ds": "r3.univar", + }[type] + # We need a database interface dbif = SQLDatabaseInterfaceConnection() dbif.connect() @@ -54,9 +68,14 @@ def print_gridded_dataset_univar_statistics( if output is not None: out_file = open(output, "w") - rows = sp.get_registered_maps("id,start_time,end_time", where, "start_time", dbif) + strds_cols = ( + "id,start_time,end_time,semantic_label" + if type == "strds" + else "id,start_time,end_time" + ) + rows = sp.get_registered_maps(strds_cols, where, "start_time", dbif) - if not rows: + if not rows and rows != [""]: dbif.close() err = "Space time %(sp)s dataset <%(i)s> is empty" if where: @@ -66,40 +85,60 @@ def print_gridded_dataset_univar_statistics( ) if no_header is False: - string = "" - string += "id" + fs + "start" + fs + "end" + fs + "mean" + fs - string += "min" + fs + "max" + fs - string += "mean_of_abs" + fs + "stddev" + fs + "variance" + fs - string += "coeff_var" + fs + "sum" + fs + "null_cells" + fs + "cells" - string += fs + "non_null_cells" + cols = ( + ["id", "semantic_label", "start", "end"] + if type == "strds" + else ["id", "start", "end"] + ) + if zones: + cols.append("zone") + cols.extend( + [ + "mean", + "min", + "max", + "mean_of_abs", + "stddev", + "variance", + "coeff_var", + "sum", + "null_cells", + "cells", + "non_null_cells", + ] + ) if extended is True: - string += fs + "first_quartile" + fs + "median" + fs - string += "third_quartile" + fs + "percentile_90" + cols.extend(["first_quartile", "median", "third_quartile", "percentile_90"]) + string = fs.join(cols) if output is None: print(string) else: out_file.write(string + "\n") + flag = "g" + + if extended is True: + flag += "e" + if type == "strds" and rast_region is True: + flag += "r" + for row in rows: string = "" id = row["id"] start = row["start_time"] end = row["end_time"] + semantic_label = ( + "" + if type != "strds" or not row["semantic_label"] + else row["semantic_label"] + ) - flag = "g" - - if extended is True: - flag += "e" - if type == "strds" and rast_region is True: - flag += "r" - - if type == "strds": - stats = gscript.parse_command("r.univar", map=id, flags=flag) - elif type == "str3ds": - stats = gscript.parse_command("r3.univar", map=id, flags=flag) + univar_stats = gscript.read_command( + stats_module, map=id, flags=flag, zones=zones + ).rstrip() - if not stats: + if not univar_stats: if type == "strds": gscript.warning( _("Unable to get statistics for raster map " "<%s>") % id @@ -109,19 +148,36 @@ def print_gridded_dataset_univar_statistics( _("Unable to get statistics for 3d raster map" " <%s>") % id ) continue + eol = "" - string += str(id) + fs + str(start) + fs + str(end) - string += fs + str(stats["mean"]) + fs + str(stats["min"]) - string += fs + str(stats["max"]) + fs + str(stats["mean_of_abs"]) - string += fs + str(stats["stddev"]) + fs + str(stats["variance"]) - string += fs + str(stats["coeff_var"]) + fs + str(stats["sum"]) - string += fs + str(stats["null_cells"]) + fs + str(stats["cells"]) - string += fs + str(int(stats["cells"]) - int(stats["null_cells"])) - if extended is True: - string += fs + str(stats["first_quartile"]) + fs + str(stats["median"]) + for idx, stats_kv in enumerate(univar_stats.split(";")): + stats = gscript.utils.parse_key_val(stats_kv) string += ( - fs + str(stats["third_quartile"]) + fs + str(stats["percentile_90"]) + f"{id}{fs}{semantic_label}{fs}{start}{fs}{end}" + if type == "strds" + else f"{id}{fs}{start}{fs}{end}" ) + if zones: + if idx == 0: + zone = str(stats["zone"]) + string = "" + continue + string += f"{fs}{zone}" + if "zone" in stats: + zone = str(stats["zone"]) + eol = "\n" + else: + eol = "" + string += f'{fs}{stats["mean"]}{fs}{stats["min"]}' + string += f'{fs}{stats["max"]}{fs}{stats["mean_of_abs"]}' + string += f'{fs}{stats["stddev"]}{fs}{stats["variance"]}' + string += f'{fs}{stats["coeff_var"]}{fs}{stats["sum"]}' + string += f'{fs}{stats["null_cells"]}{fs}{stats["n"]}' + string += f'{fs}{stats["n"]}' + if extended is True: + string += f'{fs}{stats["first_quartile"]}{fs}{stats["median"]}' + string += f'{fs}{stats["third_quartile"]}{fs}{stats["percentile_90"]}' + string += eol if output is None: print(string) diff --git a/temporal/t.rast.univar/t.rast.univar.html b/temporal/t.rast.univar/t.rast.univar.html index 37ec56d2d5f..41b4e9527b5 100644 --- a/temporal/t.rast.univar/t.rast.univar.html +++ b/temporal/t.rast.univar/t.rast.univar.html @@ -7,10 +7,15 @@

DESCRIPTION

By default it returns the name of the map, the start and end date of dataset and the following values: mean, minimum and maximum vale, mean_of_abs, standard deviation, variance, coeff_var, number of null -cells, total number of cell. +cells, total number of cells.

Using the e flag it can calculate also extended statistics: first quartile, median value, third quartile and percentile 90. +

+If a zones raster map is provided, statistics are computed for +each zone (category) in that input raster map. The zones option +does not support Spatio-Temporal-Raster-Datasets (STRDS) but only a single, +static raster map.

EXAMPLE

@@ -32,8 +37,10 @@

SEE ALSO

t.create, t.info +t.create,

AUTHOR

Sören Gebbert, Thünen Institute of Climate-Smart Agriculture +Stefan Blumentrath, (Support for zones) diff --git a/temporal/t.rast.univar/t.rast.univar.py b/temporal/t.rast.univar/t.rast.univar.py index c7da2f4dec4..71f05213e1a 100755 --- a/temporal/t.rast.univar/t.rast.univar.py +++ b/temporal/t.rast.univar/t.rast.univar.py @@ -31,6 +31,12 @@ # %option G_OPT_STRDS_INPUT # %end +# %option G_OPT_R_INPUT +# % key: zones +# % description: Raster map used for zoning, must be of type CELL +# % required: no +# %end + # %option G_OPT_F_OUTPUT # % required: no # %end @@ -60,24 +66,27 @@ # % guisection: Formatting # %end -import grass.script as grass - +import grass.script as gs ############################################################################ def main(): + # Get the options and flags + options, flags = gs.parser() + # lazy imports import grass.temporal as tgis - # Get the options + # Define variables input = options["input"] + zones = options["zones"] output = options["output"] where = options["where"] extended = flags["e"] no_header = flags["u"] rast_region = bool(flags["r"]) - separator = grass.separator(options["separator"]) + separator = gs.separator(options["separator"]) # Make sure the temporal database exists tgis.init() @@ -87,11 +96,23 @@ def main(): if output == "-": output = None + # Check if zones map exists and is of type CELL + if zones: + if gs.raster.raster_info(zones)["datatype"] != "CELL": + gs.fatal(_("Zoning raster must be of type CELL")) + tgis.print_gridded_dataset_univar_statistics( - "strds", input, output, where, extended, no_header, separator, rast_region + "strds", + input, + output, + where, + extended, + no_header=no_header, + fs=separator, + rast_region=rast_region, + zones=zones, ) if __name__ == "__main__": - options, flags = grass.parser() main() diff --git a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py index 6c886a2e276..946a9a1152b 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -19,11 +19,37 @@ def setUpClass(cls): cls.use_temp_region() cls.runModule("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=1, res3=1) + cls.runModule( + "r.mapcalc", + expression="zones = int(if(row() <= 5, 1, if(row() >= 20, 3, 2)))", + overwrite=True, + ) + cls.runModule("r.mapcalc", expression="a_1 = 100", overwrite=True) cls.runModule("r.mapcalc", expression="a_2 = 200", overwrite=True) cls.runModule("r.mapcalc", expression="a_3 = 300", overwrite=True) cls.runModule("r.mapcalc", expression="a_4 = 400", overwrite=True) + cls.runModule("r.mapcalc", expression="b_1 = 110", overwrite=True) + cls.runModule("r.mapcalc", expression="b_2 = 220", overwrite=True) + cls.runModule("r.mapcalc", expression="b_3 = 330", overwrite=True) + cls.runModule("r.mapcalc", expression="b_4 = 440", overwrite=True) + + cls.runModule("r.mapcalc", expression="c_1 = 111", overwrite=True) + cls.runModule("r.mapcalc", expression="c_2 = 222", overwrite=True) + cls.runModule("r.mapcalc", expression="c_3 = 333", overwrite=True) + cls.runModule("r.mapcalc", expression="c_4 = 444", overwrite=True) + + cls.runModule("r.support", map="b_1", semantic_label="S2_B1", overwrite=True) + cls.runModule("r.support", map="b_2", semantic_label="S2_B1", overwrite=True) + cls.runModule("r.support", map="b_3", semantic_label="S2_B1", overwrite=True) + cls.runModule("r.support", map="b_4", semantic_label="S2_B1", overwrite=True) + + cls.runModule("r.support", map="c_1", semantic_label="S2_C1", overwrite=True) + cls.runModule("r.support", map="c_2", semantic_label="S2_C1", overwrite=True) + cls.runModule("r.support", map="c_3", semantic_label="S2_C1", overwrite=True) + cls.runModule("r.support", map="c_4", semantic_label="S2_C1", overwrite=True) + cls.runModule( "t.create", type="strds", @@ -33,6 +59,15 @@ def setUpClass(cls): description="A test", overwrite=True, ) + cls.runModule( + "t.create", + type="strds", + temporaltype="absolute", + output="B", + title="B test", + description="B test", + overwrite=True, + ) cls.runModule( "t.register", flags="i", @@ -43,14 +78,37 @@ def setUpClass(cls): increment="3 months", overwrite=True, ) + cls.runModule( + "t.register", + flags="i", + type="raster", + input="B", + maps="b_1,b_2,b_3,b_4", + start="2001-01-01", + increment="3 months", + overwrite=True, + ) + cls.runModule( + "t.register", + flags="i", + type="raster", + input="B", + maps="c_1,c_2,c_3,c_4", + start="2001-01-01", + increment="3 months", + overwrite=True, + ) @classmethod def tearDownClass(cls): """Remove the temporary region""" cls.runModule("t.remove", flags="df", type="strds", inputs="A") + cls.runModule("t.remove", flags="df", type="strds", inputs="B") + cls.runModule("g.remove", flags="f", type="raster", name="zones") + cls.del_temp_region() - def test_1(self): + def test_with_all_maps(self): t_rast_univar = SimpleModule( "t.rast.univar", @@ -62,11 +120,11 @@ def test_1(self): self.runModule("g.region", res=1) self.assertModule(t_rast_univar) - univar_text = """id|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells -a_1@testing|2001-01-01 00:00:00|2001-04-01 00:00:00|100|100|100|100|0|0|0|960000|0|9600|9600 -a_2@testing|2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 -a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 -a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 + univar_text = """id|semantic_label|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +a_1@testing||2001-01-01 00:00:00|2001-04-01 00:00:00|100|100|100|100|0|0|0|960000|0|9600|9600 +a_2@testing||2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 +a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 +a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 """ for ref, res in zip( univar_text.split("\n"), t_rast_univar.outputs.stdout.split("\n") @@ -76,7 +134,7 @@ def test_1(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_2(self): + def test_with_subset_of_maps(self): t_rast_univar = SimpleModule( "t.rast.univar", @@ -88,10 +146,10 @@ def test_2(self): self.runModule("g.region", res=1) self.assertModule(t_rast_univar) - univar_text = """id|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells -a_2@testing|2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 -a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 -a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 + univar_text = """id|semantic_label|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +a_2@testing||2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 +a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 +a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 """ for ref, res in zip( univar_text.split("\n"), t_rast_univar.outputs.stdout.split("\n") @@ -101,7 +159,7 @@ def test_2(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_3(self): + def test_coarser_resolution(self): t_rast_univar = SimpleModule( "t.rast.univar", @@ -113,10 +171,10 @@ def test_3(self): self.runModule("g.region", res=10) self.assertModule(t_rast_univar) - univar_text = """id|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells -a_2@testing|2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|19200|0|96|96 -a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|28800|0|96|96 -a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|38400|0|96|96 + univar_text = """id|semantic_label|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +a_2@testing||2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|19200|0|96|96 +a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|28800|0|96|96 +a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|38400|0|96|96 """ for ref, res in zip( univar_text.split("\n"), t_rast_univar.outputs.stdout.split("\n") @@ -126,7 +184,7 @@ def test_3(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_4(self): + def test_subset_with_output(self): self.runModule("g.region", res=10) self.assertModule( @@ -139,10 +197,10 @@ def test_4(self): verbose=True, ) - univar_text = """id|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells -a_2@testing|2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 -a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 -a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 + univar_text = """id|semantic_label|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +a_2@testing||2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 +a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 +a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 """ univar_output = open("univar_output.txt", "r").read() @@ -154,7 +212,7 @@ def test_4(self): print(type(res_line)) self.assertLooksLike(ref_line, res_line) - def test_5(self): + def test_subset_with_output_coarse_resolution(self): self.runModule("g.region", res=10) self.assertModule( @@ -167,9 +225,9 @@ def test_5(self): verbose=True, ) - univar_text = """a_2@testing|2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 -a_3@testing|2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 -a_4@testing|2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 + univar_text = """a_2@testing||2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|1920000|0|9600|9600 +a_3@testing||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|2880000|0|9600|9600 +a_4@testing||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|3840000|0|9600|9600 """ univar_output = open("univar_output.txt", "r").read() @@ -179,7 +237,7 @@ def test_5(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_6_error_handling_empty_strds(self): + def test_error_handling_empty_strds(self): # Empty strds self.assertModuleFail( "t.rast.univar", @@ -190,10 +248,75 @@ def test_6_error_handling_empty_strds(self): verbose=True, ) - def test_7_error_handling_no_input(self): + def test_error_handling_no_input(self): # No input self.assertModuleFail("t.rast.univar", output="out.txt") + def test_with_zones(self): + """Test use of zones""" + + t_rast_univar = SimpleModule( + "t.rast.univar", + input="A", + where="start_time >= '2001-01-01'", + zones="zones", + overwrite=True, + verbose=True, + ) + self.runModule("g.region", res=1) + self.assertModule(t_rast_univar) + + print(t_rast_univar.outputs.stdout.split("\n")) + + univar_text = """id|semantic_label|start|end|zone|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +a_1@PERMANENT||2001-01-01 00:00:00|2001-04-01 00:00:00|1|100|100|100|100|0|0|0|60000|0|600|600 +a_1@PERMANENT||2001-01-01 00:00:00|2001-04-01 00:00:00|2|100|100|100|100|0|0|0|168000|0|1680|1680 +a_1@PERMANENT||2001-01-01 00:00:00|2001-04-01 00:00:00|3|100|100|100|100|0|0|0|732000|0|7320|7320 +a_2@PERMANENT||2001-04-01 00:00:00|2001-07-01 00:00:00|1|200|200|200|200|0|0|0|120000|0|600|600 +a_2@PERMANENT||2001-04-01 00:00:00|2001-07-01 00:00:00|2|200|200|200|200|0|0|0|336000|0|1680|1680 +a_2@PERMANENT||2001-04-01 00:00:00|2001-07-01 00:00:00|3|200|200|200|200|0|0|0|1464000|0|7320|7320 +a_3@PERMANENT||2001-07-01 00:00:00|2001-10-01 00:00:00|1|300|300|300|300|0|0|0|180000|0|600|600 +a_3@PERMANENT||2001-07-01 00:00:00|2001-10-01 00:00:00|2|300|300|300|300|0|0|0|504000|0|1680|1680 +a_3@PERMANENT||2001-07-01 00:00:00|2001-10-01 00:00:00|3|300|300|300|300|0|0|0|2196000|0|7320|7320 +a_4@PERMANENT||2001-10-01 00:00:00|2002-01-01 00:00:00|1|400|400|400|400|0|0|0|240000|0|600|600 +a_4@PERMANENT||2001-10-01 00:00:00|2002-01-01 00:00:00|2|400|400|400|400|0|0|0|672000|0|1680|1680 +a_4@PERMANENT||2001-10-01 00:00:00|2002-01-01 00:00:00|3|400|400|400|400|0|0|0|2928000|0|7320|7320 +""" + + for ref, res in zip( + univar_text.split("\n"), t_rast_univar.outputs.stdout.split("\n") + ): + if ref and res: + ref_line = ref.split("|", 1)[1] + res_line = res.split("|", 1)[1] + self.assertLooksLike(ref_line, res_line) + + def test_with_semantic_label(self): + """Test semantic labels""" + t_rast_univar = SimpleModule( + "t.rast.univar", + input="B.S2_B1", + where="start_time >= '2001-01-01'", + overwrite=True, + verbose=True, + ) + self.runModule("g.region", res=1) + self.assertModule(t_rast_univar) + + univar_text = """id|semantic_label|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +b_1@PERMANENT|S2_B1|2001-01-01 00:00:00|2001-04-01 00:00:00|110|110|110|110|0|0|0|1056000|0|9600|9600 +b_2@PERMANENT|S2_B1|2001-04-01 00:00:00|2001-07-01 00:00:00|220|220|220|220|0|0|0|2112000|0|9600|9600 +b_3@PERMANENT|S2_B1|2001-07-01 00:00:00|2001-10-01 00:00:00|330|330|330|330|0|0|0|3168000|0|9600|9600 +b_4@PERMANENT|S2_B1|2001-10-01 00:00:00|2002-01-01 00:00:00|440|440|440|440|0|0|0|4224000|0|9600|9600 +""" + for ref, res in zip( + univar_text.split("\n"), t_rast_univar.outputs.stdout.split("\n") + ): + if ref and res: + ref_line = ref.split("|", 1)[1] + res_line = res.split("|", 1)[1] + self.assertLooksLike(ref_line, res_line) + if __name__ == "__main__": from grass.gunittest.main import test diff --git a/temporal/t.rast3d.univar/t.rast3d.univar.html b/temporal/t.rast3d.univar/t.rast3d.univar.html index 9144911ea37..42a59843f85 100644 --- a/temporal/t.rast3d.univar/t.rast3d.univar.html +++ b/temporal/t.rast3d.univar/t.rast3d.univar.html @@ -16,3 +16,4 @@

SEE ALSO

AUTHOR

Sören Gebbert, Thünen Institute of Climate-Smart Agriculture +Stefan Blumentrath, (Support for zones) diff --git a/temporal/t.rast3d.univar/t.rast3d.univar.py b/temporal/t.rast3d.univar/t.rast3d.univar.py index 9a228d1234b..c3ad1503b0e 100755 --- a/temporal/t.rast3d.univar/t.rast3d.univar.py +++ b/temporal/t.rast3d.univar/t.rast3d.univar.py @@ -33,6 +33,13 @@ # %option G_OPT_STR3DS_INPUT # %end +# %option G_OPT_R_INPUT +# % key: zones +# % label: Raster map withh zones to compute statistics for +# % description: Raster map withh zones to compute statistics for (needs to be CELL) +# % required: no +# %end + # %option G_OPT_F_OUTPUT # % required: no # %end @@ -57,23 +64,27 @@ # % guisection: Formatting # %end -import grass.script as grass +import grass.script as gs ############################################################################ def main(): + # Get the options + options, flags = gs.parser() + # lazy imports import grass.temporal as tgis - # Get the options + # Define variables input = options["input"] + zones = options["zones"] output = options["output"] where = options["where"] extended = flags["e"] no_header = flags["s"] - separator = grass.separator(options["separator"]) + separator = gs.separator(options["separator"]) # Make sure the temporal database exists tgis.init() @@ -83,11 +94,19 @@ def main(): if output == "-": output = None + # Check if zones map exists and is of type CELL tgis.print_gridded_dataset_univar_statistics( - "str3ds", input, output, where, extended, no_header, separator + "str3ds", + input, + output, + where, + extended, + no_header=no_header, + fs=separator, + rast_region=False, + zones=zones, ) if __name__ == "__main__": - options, flags = grass.parser() main() diff --git a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py index e9b0e9afc65..df823f73d48 100644 --- a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py +++ b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py @@ -19,6 +19,12 @@ def setUpClass(cls): cls.use_temp_region() cls.runModule("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=1, res3=1) + cls.runModule( + "r3.mapcalc", + expression="zones = int(if(row() <= 5, 1, if(row() >= 20, 3, 2)))", + overwrite=True, + ) + cls.runModule("r3.mapcalc", expression="a_1 = 100", overwrite=True) cls.runModule("r3.mapcalc", expression="a_2 = 200", overwrite=True) cls.runModule("r3.mapcalc", expression="a_3 = 300", overwrite=True) @@ -48,9 +54,10 @@ def setUpClass(cls): def tearDownClass(cls): """Remove the temporary region""" cls.runModule("t.remove", flags="df", type="str3ds", inputs="A") + cls.runModule("g.remove", flags="f", type="raster_3d", name="zones") cls.del_temp_region() - def test_1(self): + def test_with_all_maps(self): t_rast3d_univar = SimpleModule( "t.rast3d.univar", @@ -75,7 +82,7 @@ def test_1(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_2(self): + def test_with_subset_of_maps(self): t_rast3d_univar = SimpleModule( "t.rast3d.univar", @@ -99,7 +106,7 @@ def test_2(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_3(self): + def test_subset_with_output(self): self.assertModule( "t.rast3d.univar", @@ -123,7 +130,7 @@ def test_3(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_4(self): + def test_subset_with_output_no_header(self): self.assertModule( "t.rast3d.univar", @@ -147,7 +154,7 @@ def test_4(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_5_error_handling_empty_strds(self): + def test_error_handling_empty_strds(self): # Empty str3ds self.assertModuleFail( "t.rast3d.univar", @@ -158,10 +165,47 @@ def test_5_error_handling_empty_strds(self): verbose=True, ) - def test_6_error_handling_no_input(self): + def test_error_handling_no_input(self): # No input self.assertModuleFail("t.rast3d.univar", output="out.txt") + def test_with_zones(self): + """Test use of zones""" + + t_rast_univar_zones = SimpleModule( + "t.rast3d.univar", + input="A", + where="start_time >= '2001-01-01'", + zones="zones", + overwrite=True, + verbose=True, + ) + self.runModule("g.region", res=1) + self.assertModule(t_rast_univar_zones) + + univar_text = """id|start|end|zone|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +a_1@PERMANENT|2001-01-01 00:00:00|2001-04-01 00:00:00|1|100|100|100|100|0|0|0|3000000|0|30000|30000 +a_1@PERMANENT|2001-01-01 00:00:00|2001-04-01 00:00:00|2|100|100|100|100|0|0|0|8400000|0|84000|84000 +a_1@PERMANENT|2001-01-01 00:00:00|2001-04-01 00:00:00|3|100|100|100|100|0|0|0|36600000|0|366000|366000 +a_2@PERMANENT|2001-04-01 00:00:00|2001-07-01 00:00:00|1|200|200|200|200|0|0|0|6000000|0|30000|30000 +a_2@PERMANENT|2001-04-01 00:00:00|2001-07-01 00:00:00|2|200|200|200|200|0|0|0|16800000|0|84000|84000 +a_2@PERMANENT|2001-04-01 00:00:00|2001-07-01 00:00:00|3|200|200|200|200|0|0|0|73200000|0|366000|366000 +a_3@PERMANENT|2001-07-01 00:00:00|2001-10-01 00:00:00|1|300|300|300|300|0|0|0|9000000|0|30000|30000 +a_3@PERMANENT|2001-07-01 00:00:00|2001-10-01 00:00:00|2|300|300|300|300|0|0|0|25200000|0|84000|84000 +a_3@PERMANENT|2001-07-01 00:00:00|2001-10-01 00:00:00|3|300|300|300|300|0|0|0|109800000|0|366000|366000 +a_4@PERMANENT|2001-10-01 00:00:00|2002-01-01 00:00:00|1|400|400|400|400|0|0|0|12000000|0|30000|30000 +a_4@PERMANENT|2001-10-01 00:00:00|2002-01-01 00:00:00|2|400|400|400|400|0|0|0|33600000|0|84000|84000 +a_4@PERMANENT|2001-10-01 00:00:00|2002-01-01 00:00:00|3|400|400|400|400|0|0|0|146400000|0|366000|366000 +""" + + for ref, res in zip( + univar_text.split("\n"), t_rast_univar_zones.outputs.stdout.split("\n") + ): + if ref and res: + ref_line = ref.split("|", 1)[1] + res_line = res.split("|", 1)[1] + self.assertLooksLike(ref_line, res_line) + if __name__ == "__main__": from grass.gunittest.main import test