Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v.univar: add JSON support #3784

Merged
merged 5 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion vector/v.univar/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ MODULE_TOPDIR = ../..

PGM=v.univar

LIBES = $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB)
LIBES = $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB) $(PARSONLIB)
DEPENDENCIES = $(VECTORDEP) $(DBMIDEP) $(GISDEP)
EXTRA_INC = $(VECT_INC)
EXTRA_CFLAGS = $(VECT_CFLAGS)
Expand Down
92 changes: 89 additions & 3 deletions vector/v.univar/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@
#include <grass/vector.h>
#include <grass/dbmi.h>
#include <grass/glocale.h>
#include <grass/parson.h>

static void select_from_geometry(void);
static void select_from_database(void);
static void summary(void);

enum OutputFormat { PLAIN, JSON };

struct Option *field_opt, *where_opt, *col_opt;
struct Flag *shell_flag, *ext_flag, *weight_flag, *geometry;
struct Map_info Map;
Expand Down Expand Up @@ -80,10 +83,12 @@ double total_size = 0.0; /* total size: length/area */
/* Extended statistics */
int perc;

enum OutputFormat format;

int main(int argc, char *argv[])
{
struct GModule *module;
struct Option *map_opt, *type_opt, *percentile;
struct Option *map_opt, *type_opt, *percentile, *format_opt;

module = G_define_module();
G_add_keyword(_("vector"));
Expand Down Expand Up @@ -136,6 +141,9 @@ int main(int argc, char *argv[])
geometry->description =
_("Calculate geometric distances instead of attribute statistics");

format_opt = G_define_standard_option(G_OPT_F_FORMAT);
format_opt->guisection = _("Print");

G_gisinit(argv[0]);

if (G_parser(argc, argv))
Expand All @@ -147,6 +155,13 @@ int main(int argc, char *argv[])
col_opt->key, col_opt->description);
}

if (strcmp(format_opt->answer, "json") == 0) {
format = JSON;
}
else {
format = PLAIN;
}

otype = Vect_option_to_types(type_opt);
perc = atoi(percentile->answer);

Expand Down Expand Up @@ -546,6 +561,9 @@ void select_from_database(void)

void summary(void)
{
JSON_Value *root_value;
JSON_Object *root_object;

if (compatible) {
if (!geometry->answer && weight_flag->answer) {
mean = sum / total_size;
Expand Down Expand Up @@ -582,7 +600,49 @@ void summary(void)

G_debug(3, "otype %d:", otype);

if (shell_flag->answer) {
if (format == JSON) {
root_value = json_value_init_object();
if (root_value == NULL) {
G_fatal_error(
_("Failed to initialize JSON object. Out of memory?"));
}
root_object = json_object(root_value);

json_object_set_number(root_object, "n", count);
if (geometry->answer) {
json_object_set_number(root_object, "nzero", nzero);
}
else {
json_object_set_number(root_object, "missing", nmissing);
json_object_set_number(root_object, "nnull", nnull);
}
if (count > 0) {
json_object_set_number(root_object, "min", min);
json_object_set_number(root_object, "max", max);
json_object_set_number(root_object, "range", max - min);
json_object_set_number(root_object, "sum", sum);
if (compatible) {
json_object_set_number(root_object, "mean", mean);
json_object_set_number(root_object, "mean_abs", mean_abs);
if (geometry->answer || !weight_flag->answer) {
json_object_set_number(root_object, "population_stddev",
pop_stdev);
json_object_set_number(root_object, "population_variance",
pop_variance);
json_object_set_number(root_object,
"population_coeff_variation",
pop_coeff_variation);
json_object_set_number(root_object, "sample_stddev",
sample_stdev);
json_object_set_number(root_object, "sample_variance",
sample_variance);
json_object_set_number(root_object, "kurtosis", kurtosis);
json_object_set_number(root_object, "skewness", skewness);
}
}
}
}
else if (shell_flag->answer) {
fprintf(stdout, "n=%d\n", count);
if (geometry->answer) {
fprintf(stdout, "nzero=%d\n", nzero);
Expand Down Expand Up @@ -688,7 +748,23 @@ void summary(void)
quartile_perc = (Cvarr.value[qpos_perc]).val.d;
}

if (shell_flag->answer) {
if (format == JSON) {
json_object_set_number(root_object, "first_quartile", quartile_25);
json_object_set_number(root_object, "median", median);
json_object_set_number(root_object, "third_quartile", quartile_75);

JSON_Value *percentiles_array_value = json_value_init_array();
JSON_Array *percentiles_array = json_array(percentiles_array_value);
JSON_Value *percentile_value = json_value_init_object();
JSON_Object *percentile_object = json_object(percentile_value);

json_object_set_number(percentile_object, "percentile", perc);
json_object_set_number(percentile_object, "value", quartile_perc);
json_array_append_value(percentiles_array, percentile_value);
json_object_set_value(root_object, "percentiles",
percentiles_array_value);
}
else if (shell_flag->answer) {
fprintf(stdout, "first_quartile=%g\n", quartile_25);
fprintf(stdout, "median=%g\n", median);
fprintf(stdout, "third_quartile=%g\n", quartile_75);
Expand All @@ -712,4 +788,14 @@ void summary(void)
fprintf(stdout, "%dth percentile: %g\n", perc, quartile_perc);
}
}

if (format == JSON) {
char *serialized_string = json_serialize_to_string_pretty(root_value);
if (serialized_string == NULL) {
G_fatal_error(_("Failed to initialize pretty JSON string."));
}
puts(serialized_string);
json_free_serialized_string(serialized_string);
json_value_free(root_value);
}
}
44 changes: 44 additions & 0 deletions vector/v.univar/testsuite/v_univar_test.py
kritibirda26 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
for details.
"""

import json
from itertools import zip_longest

from grass.gunittest.case import TestCase
from grass.gunittest.main import test
from grass.gunittest.gmodules import SimpleModule
Expand Down Expand Up @@ -126,6 +129,47 @@ def test_output2(self):
map="roadsmajor", column="MAJORRDS_", reference=univar_string, precision=3
)

def test_json(self):
"""Testing output in JSON fomrat"""
module = SimpleModule(
"v.univar", map="geology", column="PERIMETER", flags="e", format="json"
)
self.runModule(module)

expected = {
"n": 1832,
"missing": 0,
"nnull": 0,
"min": 166.946991,
"max": 2729482.25,
"range": 2729315.3030090001,
"sum": 78876146.145385057,
"mean": 43054.664926520229,
"mean_abs": 43054.664926520229,
"population_stddev": 132689.08650029532,
"population_variance": 17606393676.282852,
"population_coeff_variation": 3.0818747916573215,
"sample_stddev": 132725.31560308655,
"sample_variance": 17616009401.938931,
"kurtosis": 139.15698418811229,
"skewness": 9.7065048189730767,
"first_quartile": 3699.3234859999998,
"median": 10308.4453125,
"third_quartile": 29259.074218999998,
"percentiles": [{"percentile": 90, "value": 86449.734375}],
}
results = json.loads(module.outputs.stdout)

expected_percentiles = expected.pop("percentiles")
result_percentiles = results.pop("percentiles")
for p1, p2 in zip_longest(expected_percentiles, result_percentiles):
self.assertEqual(p1["percentile"], p2["percentile"])
self.assertAlmostEqual(p1["value"], p2["value"])

self.assertCountEqual(list(expected.keys()), list(results.keys()))
for key in expected:
self.assertAlmostEqual(expected[key], results[key])


if __name__ == "__main__":
test()
35 changes: 35 additions & 0 deletions vector/v.univar/v.univar.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,41 @@ <h3>Calculate statistic of distance between sampling points</h3>
skewness: 0.238688
</pre></div>

<h3>Output in JSON format</h3>
<div class="code"><pre>
v.univar -e samples column=heights type=point format=json
</pre></div>
will output the results in JSON format:
<div class="code"><pre>
{
"n": 1832,
"missing": 0,
"nnull": 0,
"min": 166.946991,
"max": 2729482.25,
"range": 2729315.3030090001,
"sum": 78876146.145385057,
"mean": 43054.664926520229,
"mean_abs": 43054.664926520229,
"population_stddev": 132689.08650029532,
"population_variance": 17606393676.282852,
"population_coeff_variation": 3.0818747916573215,
"sample_stddev": 132725.31560308655,
"sample_variance": 17616009401.938931,
"kurtosis": 139.15698418811229,
"skewness": 9.7065048189730767,
"first_quartile": 3699.3234859999998,
"median": 10308.4453125,
"third_quartile": 29259.074218999998,
"percentiles": [
{
"percentile": 90,
"value": 86449.734375
}
]
}
</pre></div>

<h2>SEE ALSO</h2>

<em>
Expand Down
Loading