From 331414d7d17e8452a1644b7584ae31e902a70b39 Mon Sep 17 00:00:00 2001 From: Stefan Blumentrath Date: Fri, 29 Sep 2023 14:15:25 +0200 Subject: [PATCH] t.rast.univar: Add region_relation option for spatial filtering STDS by computational region (#2793) * add spatial extent filter * pass only spatial relation * add spatial extent filter * fix 3D where * add tests for spatial filter * add more tests for spatial filter * fix failing test * add spatial relation doc * lower message severity * spatial_relation intro * Update temporal/t.rast.univar/t.rast.univar.html Co-authored-by: Veronica Andreo * Update temporal/temporalintro.html Co-authored-by: Veronica Andreo * rewrite user message * fix tests for empty strds * rename spatial filter option * rename spatial filter option * update r-flag description * rename spatial filter option * rename spatial filter option * improve spatial filter docs * fix failing test * Update python/grass/temporal/abstract_space_time_dataset.py Co-authored-by: Vaclav Petras * Update python/grass/temporal/univar_statistics.py Co-authored-by: Vaclav Petras * address code review * address code review * address code review, some linting * add back percentiles * black --------- Co-authored-by: Veronica Andreo Co-authored-by: Vaclav Petras --- .../temporal/abstract_space_time_dataset.py | 86 ++++++--- python/grass/temporal/univar_statistics.py | 48 ++++- temporal/t.rast.univar/t.rast.univar.html | 26 ++- temporal/t.rast.univar/t.rast.univar.py | 17 +- .../testsuite/test_t_rast_univar.py | 174 +++++++++++++----- .../testsuite/test_t_rast3d_univar.py | 4 +- temporal/temporalintro.html | 18 ++ 7 files changed, 282 insertions(+), 91 deletions(-) diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 2f956ffb268..aefe47c51f5 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -1367,15 +1367,22 @@ def get_registered_maps_as_objects_with_gaps( In case more map information are needed, use the select() method for each listed object. + The combination of the spatial_extent and spatial_relation parameters + can be used to return only map objects with the given spatial relation + to the provided spatial extent. + :param where: The SQL where statement to select a subset of the registered maps without "WHERE" :param dbif: The database interface to be used - :param spatial_extent: Return only maps with the provided spatial - relation to the given spatial extent (requires - spatial_relation parameter) - :param spatial_relation: Return only maps with the given spatial - relation to the provided spatial extent (requires - spatial_extent parameter) + :param spatial_extent: Spatial extent dict and projection information + e.g. from g.region -ug3 with GRASS GIS region keys + "n", "s", "e", "w", "b", "t", and "projection". + :param spatial_relation: Spatial relation to the provided + spatial extent as a string with one of the following values: + "overlaps": maps that spatially overlap ("intersect") + within the provided spatial extent + "is_contained": maps that are fully within the provided spatial extent + "contains": maps that contain (fully cover) the provided spatial extent :return: ordered object list, in case nothing found None is returned """ @@ -1436,17 +1443,24 @@ def get_registered_maps_as_objects_with_temporal_topology( In case more map information are needed, use the select() method for each listed object. + The combination of the spatial_extent and spatial_relation parameters + can be used to return only maps with the given spatial relation to + the provided spatial extent + :param where: The SQL where statement to select a subset of the registered maps without "WHERE" :param order: The SQL order statement to be used to order the objects in the list without "ORDER BY" :param dbif: The database interface to be used - :param spatial_extent: Return only maps with the provided spatial - relation to the given spatial extent (requires - spatial_relation parameter) - :param spatial_relation: Return only maps with the given spatial - relation to the provided spatial extent (requires - spatial_extent parameter) + :param spatial_extent: Spatial extent dict and projection information + e.g. from g.region -ug3 with GRASS GIS region keys + "n", "s", "e", "w", "b", "t", and "projection". + :param spatial_relation: Spatial relation to the provided + spatial extent as a string with one of the following values: + "overlaps": maps that spatially overlap ("intersect") + within the provided spatial extent + "is_contained": maps that are fully within the provided spatial extent + "contains": maps that contain (fully cover) the provided spatial extent :return: The ordered map object list, In case nothing found None is returned @@ -1483,17 +1497,24 @@ def get_registered_maps_as_objects( In case more map information are needed, use the select() method for each listed object. + The combination of the spatial_extent and spatial_relation parameters + can be used to return only maps with the given spatial relation to + the provided spatial extent + :param where: The SQL where statement to select a subset of the registered maps without "WHERE" :param order: The SQL order statement to be used to order the objects in the list without "ORDER BY" :param dbif: The database interface to be used - :param spatial_extent: Return only maps with the provided spatial - relation to the given spatial extent (requires - spatial_relation parameter) - :param spatial_relation: Return only maps with the given spatial - relation to the provided spatial extent (requires - spatial_extent parameter) + :param spatial_extent: Spatial extent dict and projection information + e.g. from g.region -ug3 with GRASS GIS region keys + "n", "s", "e", "w", "b", "t", and "projection". + :param spatial_relation: Spatial relation to the provided + spatial extent as a string with one of the following values: + "overlaps": maps that spatially overlap ("intersect") + within the provided spatial extent + "is_contained": maps that are fully within the provided spatial extent + "contains": maps that contain (fully cover) the provided spatial extent :return: The ordered map object list, In case nothing found None is returned @@ -1657,7 +1678,7 @@ def _update_where_statement_by_spatial_extent( :param str where: SQL WHERE statement to be updated :param dict spatial_extent: Spatial extent dict and projection information e.g. from g.region -ug3 - :param dict spatial_relation: Spatial relation to the provided + :param str spatial_relation: Spatial relation to the provided spatial extent as a string with one of the following values: "overlaps": maps that spatially overlap ("intersect") within the provided spatial extent @@ -1715,10 +1736,10 @@ def _update_where_statement_by_spatial_extent( if self.get_type() == "str3ds": if spatial_relation == "overlaps": spatial_where_template += " AND top > {b}" " AND bottom < {t}" - elif spatial_relation == "is_contained": - spatial_where_template += " AND top <= {t}" " AND bottom >= {b}" - elif spatial_relation == "contains": - spatial_where_template += " AND top >= {t}" " AND bottom <= {b}" + elif spatial_relation == "is_contained": + spatial_where_template += " AND top <= {t}" " AND bottom >= {b}" + elif spatial_relation == "contains": + spatial_where_template += " AND top >= {t}" " AND bottom <= {b}" spatial_where_template += ")" spatial_where_list = [spatial_where_template.format(**spatial_extent)] @@ -1757,18 +1778,25 @@ def get_registered_maps( In case columns are not specified, each row includes all columns specified in the datatype specific view. + The combination of the spatial_extent and spatial_relation parameters + can be used to return only SQL rows of maps with the given spatial + relation to the provided spatial extent + :param columns: Columns to be selected as SQL compliant string :param where: The SQL where statement to select a subset of the registered maps without "WHERE" :param order: The SQL order statement to be used to order the objects in the list without "ORDER BY" :param dbif: The database interface to be used - :param spatial_extent: Return only maps with the provided spatial - relation to the given spatial extent (requires - spatial_relation parameter) - :param spatial_relation: Return only maps with the given spatial - relation to the provided spatial extent (requires - spatial_extent parameter) + :param spatial_extent: Spatial extent dict and projection information + e.g. from g.region -ug3 with GRASS GIS region keys + "n", "s", "e", "w", "b", "t", and "projection". + :param spatial_relation: Spatial relation to the provided + spatial extent as a string with one of the following values: + "overlaps": maps that spatially overlap ("intersect") + within the provided spatial extent + "is_contained": maps that are fully within the provided spatial extent + "contains": maps that contain (fully cover) the provided spatial extent :return: SQL rows of all registered maps, In case nothing found None is returned diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index 95cd4fd9868..80cca737199 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -21,11 +21,12 @@ from multiprocessing import Pool from subprocess import PIPE +import grass.script as gs +from grass.pygrass.modules import Module + from .core import SQLDatabaseInterfaceConnection, get_current_mapset from .factory import dataset_factory from .open_stds import open_old_stds -import grass.script as gs -from grass.pygrass.modules import Module ############################################################################### @@ -115,11 +116,14 @@ def print_gridded_dataset_univar_statistics( no_header=False, fs="|", rast_region=False, + region_relation=None, zones=None, percentile=None, nprocs=1, ): - """Print univariate statistics for a space time raster or raster3d dataset + """Print univariate statistics for a space time raster or raster3d dataset. + Returns None if the space time raster dataset is empty or if applied + filters (where, region_relation) do not return any maps to process. :param type: Type of Space-Time-Dataset, must be either strds or str3ds :param input: The name of the space time dataset @@ -133,6 +137,12 @@ 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 region_relation: Process only maps with the given spatial relation + to the computational region. A string with one of the following values: + "overlaps": maps that spatially overlap ("intersect") + within the provided spatial extent + "is_contained": maps that are fully within the provided spatial extent + "contains": maps that contain (fully cover) the provided spatial extent :param zones: raster map with zones to calculate statistics for """ # We need a database interface @@ -144,22 +154,40 @@ def print_gridded_dataset_univar_statistics( if output is not None: out_file = open(output, "w") + spatial_extent = None + if region_relation: + spatial_extent = gs.parse_command("g.region", flags="3gu") + 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) + rows = sp.get_registered_maps( + strds_cols, + where, + "start_time", + dbif, + spatial_extent=spatial_extent, + spatial_relation=region_relation, + ) if not rows and rows != [""]: dbif.close() - err = "Space time %(sp)s dataset <%(i)s> is empty" - if where: - err += " or where condition does not return any maps" - gs.fatal( - _(err) % {"sp": sp.get_new_map_instance(None).get_type(), "i": sp.get_id()} + gs.verbose( + _( + "No maps found to process. " + "Space time {type} dataset <{id}> is either empty " + "or the where condition (if used) does not return any maps " + "or no maps with the requested spatial relation to the " + "computational region exist in the dataset." + ).format(type=sp.get_new_map_instance(None).get_type(), id=sp.get_id()) ) + if output is not None: + out_file.close() + return + if no_header is False: cols = ( ["id", "semantic_label", "start", "end"] @@ -316,7 +344,7 @@ def print_vector_dataset_univar_statistics( + fs ) string += "min" + fs + "max" + fs + "range" - if type == "point" or type == "centroid": + if type in ("point", "centroid"): string += ( fs + "mean" diff --git a/temporal/t.rast.univar/t.rast.univar.html b/temporal/t.rast.univar/t.rast.univar.html index 41b4e9527b5..7ecc0ed3316 100644 --- a/temporal/t.rast.univar/t.rast.univar.html +++ b/temporal/t.rast.univar/t.rast.univar.html @@ -4,18 +4,32 @@

DESCRIPTION

non-null cells for each registered raster map of a space time raster dataset.

-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 cells. +By default it returns the name of the map, the semantic label of the +map, the start and end date of the map and the following values: +mean, minimum and maximum vale, mean_of_abs, standard deviation, variance, +coeff_var, number of null 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, +does not support space time raster datasets (STRDS) but only a single, static raster map. +

+Space time raster datasets may contain raster maps with varying spatial +extent like for example series of scenes of satellite images. With the +region_relation option, computations can be limited to +maps of the space time raster dataset that have a given spatial relation +to the current computational region. Supported spatial relations are: +

    +
  • "overlaps": process only maps that spatially overlap ("intersect") +with the current computational region
  • +
  • "is_contained": process only maps that are fully within the current +computational region
  • +
  • "contains": process only maps that contain (fully cover) the current +computational region
  • +

EXAMPLE

@@ -43,4 +57,4 @@

SEE ALSO

AUTHOR

Sören Gebbert, Thünen Institute of Climate-Smart Agriculture -Stefan Blumentrath, (Support for zones) +Stefan Blumentrath, (Support for zones, parallel processing, and spatial relations) diff --git a/temporal/t.rast.univar/t.rast.univar.py b/temporal/t.rast.univar/t.rast.univar.py index 6eee74fd0ba..bd199f9e801 100755 --- a/temporal/t.rast.univar/t.rast.univar.py +++ b/temporal/t.rast.univar/t.rast.univar.py @@ -59,6 +59,15 @@ # % guisection: Selection # %end +# %option +# % key: region_relation +# % description: Process only maps with this spatial relation to the current computational region +# % guisection: Selection +# % options: overlaps,contains,is_contained +# % required: no +# % multiple: no +# %end + # %option G_OPT_F_SEP # % label: Field separator character between the output columns # % guisection: Formatting @@ -71,7 +80,7 @@ # %flag # % key: r -# % description: Ignore the current region settings and use the raster map regions for univar statistical calculation +# % description: Use the raster map regions for univar statistical calculation instead of the current region # %end # %flag @@ -103,6 +112,7 @@ def main(): output = options["output"] nprocs = int(options["nprocs"]) where = options["where"] + region_relation = options["region_relation"] extended = flags["e"] no_header = flags["u"] rast_region = bool(flags["r"]) @@ -136,11 +146,12 @@ def main(): output, where, extended, + percentile=percentile, no_header=no_header, fs=separator, - rast_region=rast_region, zones=zones, - percentile=percentile, + rast_region=rast_region, + region_relation=region_relation, nprocs=nprocs, ) 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 cd4bb4e00f1..cfdfa78ab27 100644 --- a/temporal/t.rast.univar/testsuite/test_t_rast_univar.py +++ b/temporal/t.rast.univar/testsuite/test_t_rast_univar.py @@ -13,11 +13,35 @@ class TestRasterUnivar(TestCase): + default_region = {"s": 0, "n": 80, "w": 0, "e": 120, "b": 0, "t": 50} + @classmethod def setUpClass(cls): """Initiate the temporal GIS and set the region""" cls.use_temp_region() - cls.runModule("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=1, res3=1) + for idx, region_extent in enumerate( + [ + [0, 80, 0, 120], + [-80, 0, 0, 120], + [0, 80, -120, 0], + [-80, 0, -120, 0], + ] + ): + cls.runModule( + "g.region", + s=region_extent[0], + n=region_extent[1], + w=region_extent[2], + e=region_extent[3], + b=0, + t=50, + res=1, + ) + cls.runModule( + "r.mapcalc", expression=f"d_{idx + 1} = {idx + 1}00", overwrite=True + ) + + cls.runModule("g.region", **cls.default_region, res=1, res3=1) cls.runModule( "r.mapcalc", @@ -68,6 +92,15 @@ def setUpClass(cls): description="B test", overwrite=True, ) + cls.runModule( + "t.create", + type="strds", + temporaltype="absolute", + output="C", + title="C test", + description="C test", + overwrite=True, + ) cls.runModule( "t.register", flags="i", @@ -98,6 +131,16 @@ def setUpClass(cls): increment="3 months", overwrite=True, ) + cls.runModule( + "t.register", + flags="i", + type="raster", + input="C", + maps="d_1,d_2,d_3,d_4", + start="2001-01-01", + increment="3 months", + overwrite=True, + ) @classmethod def tearDownClass(cls): @@ -116,7 +159,7 @@ def test_with_all_maps(self): overwrite=True, verbose=True, ) - self.runModule("g.region", res=1) + self.runModule("g.region", **self.default_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 @@ -141,7 +184,7 @@ def test_with_subset_of_maps(self): overwrite=True, verbose=True, ) - self.runModule("g.region", res=1) + self.runModule("g.region", **self.default_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 @@ -165,7 +208,7 @@ def test_coarser_resolution(self): overwrite=True, verbose=True, ) - self.runModule("g.region", res=10) + self.runModule("g.region", **self.default_region, res=10) 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 @@ -183,7 +226,8 @@ def test_coarser_resolution(self): self.assertLooksLike(ref_line, res_line) def test_subset_with_output(self): - self.runModule("g.region", res=10) + self.runModule("g.region", **self.default_region, res=10) + self.assertModule( "t.rast.univar", input="A", @@ -233,38 +277,7 @@ def test_subset_with_extended_statistics_and_output(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_subset_with_extended_statistics_output_and_zones(self): - self.runModule("g.region", res=10) - self.assertModule( - "t.rast.univar", - input="A", - flags="e", - zones="zones", - output="univar_output.txt", - where="start_time >= '2001-03-01'", - percentile=[10.0, 97.5], - overwrite=True, - verbose=True, - ) - - 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|first_quartile|median|third_quartile|percentile_10|percentile_97_5 -a_2@m2||2001-04-01 00:00:00|2001-07-01 00:00:00|2|200|200|200|200|0|0|0|4800|0|24|24|200|200|200|200|200 -a_2@m2||2001-04-01 00:00:00|2001-07-01 00:00:00|3|200|200|200|200|0|0|0|14400|0|72|72|200|200|200|200|200 -a_3@m2||2001-07-01 00:00:00|2001-10-01 00:00:00|2|300|300|300|300|0|0|0|7200|0|24|24|300|300|300|300|300 -a_3@m2||2001-07-01 00:00:00|2001-10-01 00:00:00|3|300|300|300|300|0|0|0|21600|0|72|72|300|300|300|300|300 -a_4@m2||2001-10-01 00:00:00|2002-01-01 00:00:00|2|400|400|400|400|0|0|0|9600|0|24|24|400|400|400|400|400 -a_4@m2||2001-10-01 00:00:00|2002-01-01 00:00:00|3|400|400|400|400|0|0|0|28800|0|72|72|400|400|400|400|400 -""" - univar_output = open("univar_output.txt", "r").read() - - for ref, res in zip(univar_text.split("\n"), univar_output.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_subset_with_output_coarse_resolution(self): - self.runModule("g.region", res=10) + self.runModule("g.region", **self.default_region, res=10) self.assertModule( "t.rast.univar", input="A", @@ -289,7 +302,7 @@ def test_subset_with_output_coarse_resolution(self): def test_error_handling_empty_strds(self): # Empty strds - self.assertModuleFail( + self.assertModule( "t.rast.univar", input="A", output="univar_output.txt", @@ -313,7 +326,7 @@ def test_with_zones(self): overwrite=True, verbose=True, ) - self.runModule("g.region", res=1) + self.runModule("g.region", **self.default_region, res=1) self.assertModule(t_rast_univar) 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 @@ -348,7 +361,7 @@ def test_with_semantic_label(self): overwrite=True, verbose=True, ) - self.runModule("g.region", res=1) + self.runModule("g.region", **self.default_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 @@ -375,7 +388,7 @@ def test_with_semantic_label_parallel(self): overwrite=True, verbose=True, ) - self.runModule("g.region", res=1) + self.runModule("g.region", **self.default_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 @@ -383,6 +396,85 @@ def test_with_semantic_label_parallel(self): 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) + + def test_with_spatial_filter_intersects(self): + """Test spatial filter overlaps""" + t_rast_univar = SimpleModule( + "t.rast.univar", + input="C", + where="start_time >= '2001-01-01'", + nprocs=2, + region_relation="overlaps", + overwrite=True, + verbose=True, + ) + self.runModule("g.region", res=1, s=-5, n=85, w=-5, e=125) + 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 +d_1@stbl||2001-01-01 00:00:00|2001-04-01 00:00:00|100|100|100|100|0|0|0|960000|2100|9600|9600 +d_2@stbl||2001-04-01 00:00:00|2001-07-01 00:00:00|200|200|200|200|0|0|0|120000|11100|600|600 +d_3@stbl||2001-07-01 00:00:00|2001-10-01 00:00:00|300|300|300|300|0|0|0|120000|11300|400|400 +d_4@stbl||2001-10-01 00:00:00|2002-01-01 00:00:00|400|400|400|400|0|0|0|10000|11675|25|25 +""" + 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_spatial_filter_contains(self): + """Test spatial filter contains""" + t_rast_univar = SimpleModule( + "t.rast.univar", + input="C", + where="start_time >= '2001-01-01'", + nprocs=2, + region_relation="contains", + overwrite=True, + verbose=True, + ) + self.runModule("g.region", res=1, s=5, n=75, w=5, e=115) + self.assertModule(t_rast_univar) + + print(t_rast_univar.outputs.stdout) + univar_text = """id|semantic_label|start|end|mean|min|max|mean_of_abs|stddev|variance|coeff_var|sum|null_cells|cells|non_null_cells +d_1@stbl||2001-01-01 00:00:00|2001-04-01 00:00:00|100|100|100|100|0|0|0|770000|0|7700|7700 +""" + 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_spatial_filter_is_contained(self): + """Test spatial filter is_contained""" + t_rast_univar = SimpleModule( + "t.rast.univar", + input="C", + where="start_time >= '2001-01-01'", + nprocs=2, + region_relation="is_contained", + overwrite=True, + verbose=True, + ) + self.runModule("g.region", res=1, s=-5, n=85, w=-5, e=125) + 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 +d_1@stbl||2001-01-01 00:00:00|2001-04-01 00:00:00|100|100|100|100|0|0|0|960000|2100|9600|9600 """ for ref, res in zip( univar_text.split("\n"), t_rast_univar.outputs.stdout.split("\n") 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 6cfe46091fb..d34c1f253aa 100644 --- a/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py +++ b/temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py @@ -150,9 +150,9 @@ def test_subset_with_output_no_header(self): res_line = res.split("|", 1)[1] self.assertLooksLike(ref_line, res_line) - def test_error_handling_empty_strds(self): + def test_handling_empty_strds(self): # Empty str3ds - self.assertModuleFail( + self.assertModule( "t.rast3d.univar", input="A", output="univar_output.txt", diff --git a/temporal/temporalintro.html b/temporal/temporalintro.html index 4997bf3f279..c5eca6ead2c 100644 --- a/temporal/temporalintro.html +++ b/temporal/temporalintro.html @@ -160,6 +160,24 @@

Querying and map calculation

  • t.rast.neighbors
  • +

    +Similar to the where option, selected modules like +t.rast.univar support a +region_relation option to limit computations to maps of +the space time raster dataset that have a given spatial relation to the +current computational region. Supported spatial relations are: +

      +
    • "overlaps": process only maps that spatially overlap ("intersect") +with the current computational region
    • +
    • "is_contained": process only maps that are fully within the current +computational region
    • +
    • "contains": process only maps that contain (fully cover) the current +computational region
    • +
    +Space time raster datasets may contain raster maps with varying spatial +extent like for example series of scenes of satellite images. For such +cases the region_relation option can be useful. +

    Moreover, there is v.what.strds, that uploads space-time raster dataset values at positions of vector points,