Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 67 additions & 6 deletions temporal/t.rast3d.univar/t.rast3d.univar.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
# %option G_OPT_STR3DS_INPUT
# %end

# %option G_OPT_R_INPUT
# %option G_OPT_R3_INPUT
# % key: zones
# % label: Raster map with zones to compute statistics for
# % description: Raster map with zones to compute statistics for (needs to be CELL)
# % description: 3D raster map with zones
# % required: no
# %end

Expand All @@ -49,12 +49,37 @@
# % required: no
# %end

# %option
# % key: percentile
# % type: double
# % required: no
# % multiple: yes
# % options: 0-100
# % description: Percentile to calculate (requires extended statistics flag)
# %end

# %option G_OPT_T_WHERE
# % 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
# % answer:
# % guisection: Formatting
# %end

# %option G_OPT_F_FORMAT
# % options: plain,json,csv
# % descriptions: plain;Plain text output;json;JSON (JavaScript Object Notation);csv;CSV (Comma Separated Values)
# % guisection: Formatting
# %end

Expand All @@ -69,6 +94,10 @@
# % guisection: Formatting
# %end

# %rules
# % requires: percentile,-e
# %end

import grass.script as gs

############################################################################
Expand All @@ -78,18 +107,48 @@ def main():
# Get the options
options, flags = gs.parser()

# lazy imports
import grass.temporal as tgis

# Define variables
input = options["input"]
zones = options["zones"]
output = options["output"]
nprocs = gs.resolve_nprocs(options["nprocs"])
where = options["where"]
region_relation = options["region_relation"]
extended = flags["e"]
no_header = flags["s"]
separator = gs.separator(options["separator"])
output_format = options.get("format", "plain")

if output_format == "csv":
if not separator:
separator = ","
elif len(separator) > 1:
gs.fatal(
_("A standard CSV separator (delimiter) is only one character long")
)

elif output_format == "json":
if no_header:
gs.fatal(_("Column names are always included in JSON output"))
if separator:
gs.fatal(_("Separator option is not allowed with JSON format"))

elif not separator:
separator = "|"

percentile = None
if options["percentile"]:
try:
percentile = list(map(float, options["percentile"].split(",")))
except ValueError:
gs.fatal(
_("<{}> is not valid input to the percentile option").format(
options["percentile"]
)
)

# lazy imports
import grass.temporal as tgis

# Make sure the temporal database exists
tgis.init()
Expand All @@ -99,18 +158,20 @@ 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,
percentile=percentile,
no_header=no_header,
fs=separator,
rast_region=False,
region_relation=region_relation,
zones=zones,
nprocs=nprocs,
format=output_format,
)


Expand Down
116 changes: 116 additions & 0 deletions temporal/t.rast3d.univar/testsuite/test_t_rast3d_univar.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@author Soeren Gebbert
"""

import json
from pathlib import Path

from grass.gunittest.case import TestCase
Expand Down Expand Up @@ -254,6 +255,121 @@ def test_with_zones_parallel(self):
res_line = res.split("|", 1)[1]
self.assertLooksLike(ref_line, res_line)

def test_format_csv(self):
"""Test output generation in CSV format"""
t_rast3d_univar = SimpleModule(
"t.rast3d.univar",
input="A",
where="start_time >= '2001-01-01'",
format="csv",
overwrite=True,
verbose=True,
)
self.assertModule(t_rast3d_univar)

univar_text = """id,start,end,non_null_cells,null_cells,min,max,range,mean,mean_of_abs,stddev,variance,coeff_var,sum,sum_abs
a_1@testing,2001-01-01 00:00:00,2001-04-01 00:00:00,480000,0,100,100,0,100,100,0,0,0,48000000,48000000
a_2@testing,2001-04-01 00:00:00,2001-07-01 00:00:00,480000,0,200,200,0,200,200,0,0,0,96000000,96000000
a_3@testing,2001-07-01 00:00:00,2001-10-01 00:00:00,480000,0,300,300,0,300,300,0,0,0,144000000,144000000
a_4@testing,2001-10-01 00:00:00,2002-01-01 00:00:00,480000,0,400,400,0,400,400,0,0,0,192000000,192000000
"""
for ref, res in zip(
univar_text.split("\n"),
t_rast3d_univar.outputs.stdout.split("\n"),
strict=True,
):
if ref and res:
ref_line = ref.split(",", 1)[1]
res_line = res.split(",", 1)[1]
self.assertLooksLike(ref_line, res_line)

def test_format_json(self):
"""Test output generation in JSON format"""
t_rast3d_univar = SimpleModule(
"t.rast3d.univar",
input="A",
where="start_time >= '2001-04-01 00:00:00'",
format="json",
overwrite=True,
verbose=True,
)
self.assertModule(t_rast3d_univar)

output_json = json.loads(t_rast3d_univar.outputs.stdout)

expected_json = [
{
"id": "a_2",
"start": "2001-04-01 00:00:00",
"end": "2001-07-01 00:00:00",
"mean": 200,
"min": 200,
"max": 200,
"range": 0,
"mean_of_abs": 200,
"stddev": 0,
"variance": 0,
"coeff_var": 0,
"sum": 96000000,
"null_cells": 0,
"cells": 480000,
"n": 480000,
},
{
"id": "a_3",
"start": "2001-07-01 00:00:00",
"end": "2001-10-01 00:00:00",
"mean": 300,
"min": 300,
"max": 300,
"range": 0,
"mean_of_abs": 300,
"stddev": 0,
"variance": 0,
"coeff_var": 0,
"sum": 144000000,
"null_cells": 0,
"cells": 480000,
"n": 480000,
},
{
"id": "a_4",
"start": "2001-10-01 00:00:00",
"end": "2002-01-01 00:00:00",
"mean": 400,
"min": 400,
"max": 400,
"range": 0,
"mean_of_abs": 400,
"stddev": 0,
"variance": 0,
"coeff_var": 0,
"sum": 192000000,
"null_cells": 0,
"cells": 480000,
"n": 480000,
},
]

self.assertEqual(len(output_json), len(expected_json))

for res_obj, exp_obj in zip(output_json, expected_json, strict=True):
for key in exp_obj:
if key == "id":
self.assertTrue(res_obj[key].startswith(exp_obj[key]))
else:
self.assertEqual(res_obj[key], exp_obj[key])

def test_format_validation(self):
"""Test format validation errors (e.g. JSON with separator or no header)"""
self.assertModuleFail("t.rast3d.univar", input="A", format="json", flags="s")
self.assertModuleFail(
"t.rast3d.univar", input="A", format="json", separator=","
)
self.assertModuleFail(
"t.rast3d.univar", input="A", format="csv", separator="::"
)


if __name__ == "__main__":
from grass.gunittest.main import test
Expand Down
Loading