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

r.univar: add JSON support #3783

Merged
merged 5 commits into from
Jul 3, 2024
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
4 changes: 2 additions & 2 deletions raster/r.univar/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

MODULE_TOPDIR = ../..

LIBES2 = $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
LIBES3 = $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB)
LIBES2 = $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) $(PARSONLIB)
LIBES3 = $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) $(PARSONLIB)
DEPENDENCIES = $(RASTER3DDEP) $(GISDEP) $(RASTERDEP)
EXTRA_CFLAGS = $(OPENMP_CFLAGS)
EXTRA_INC = $(OPENMP_INCPATH)
Expand Down
6 changes: 4 additions & 2 deletions raster/r.univar/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,20 @@ typedef struct {
/* command line options are the same for raster and raster3d maps */
typedef struct {
struct Option *inputfile, *zonefile, *percentile, *output_file, *separator,
*nprocs;
*nprocs, *format;
struct Flag *shell_style, *extended, *table, *use_rast_region;
} param_type;

extern param_type param;
extern zone_type zone_info;

enum OutputFormat { PLAIN, JSON };

/* fn prototypes */
void heapsort_double(double *data, size_t n);
void heapsort_float(float *data, size_t n);
void heapsort_int(int *data, size_t n);
int print_stats(univar_stat *stats);
int print_stats(univar_stat *stats, enum OutputFormat format);
int print_stats_table(univar_stat *stats);
univar_stat *create_univar_stat_struct(int map_type, int n_perc);
void free_univar_stat_struct(univar_stat *stats);
Expand Down
38 changes: 38 additions & 0 deletions raster/r.univar/r.univar.html
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,44 @@ <h3>Zonal statistics</h3>
dataset) viewed through Libre/Open Office Calc.</i>
</div>

<h3>JSON Output</h3>
<div class="code"><pre>
r.univar -e elevation percentile=98 format=json
</pre></div>
will output the results in JSON format:

<div class="code"><pre>
[
{
"n": 2025000,
"null_cells": 0,
"cells": 2025000,
"min": 55.578792572021484,
"max": 156.32986450195312,
"range": 100.75107192993164,
"mean": 110.37544027560575,
"mean_of_abs": 110.37544027560575,
"stddev": 20.315323320598083,
"variance": 412.7123616204363,
"coeff_var": 18.40565552433679,
"sum": 223510266.55810165,
"first_quartile": 94.789985656738281,
"median": 108.87990570068359,
"third_quartile": 126.79196929931641,
"percentiles": [
{
"percentile": 98,
"value": 147.7265625
},
{
"percentile": 9,
"value": 83.494270324707031
}
]
}
]
</pre></div>

<h2>TODO</h2>

To be implemented <i>mode, skewness, kurtosis</i>.
Expand Down
14 changes: 13 additions & 1 deletion raster/r.univar/r.univar_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ void set_params(void)
_("Table output format instead of standard output format");
param.table->guisection = _("Formatting");

param.format = G_define_standard_option(G_OPT_F_FORMAT);
param.format->guisection = _("Print");

param.use_rast_region = G_define_flag();
param.use_rast_region->key = 'r';
param.use_rast_region->description =
Expand Down Expand Up @@ -140,6 +143,8 @@ int main(int argc, char *argv[])
const char *mapset, *name;
int t;

enum OutputFormat format;

G_gisinit(argv[0]);

module = G_define_module();
Expand Down Expand Up @@ -175,6 +180,13 @@ int main(int argc, char *argv[])
}
}

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

/* set nprocs parameter */
int nprocs;
sscanf(param.nprocs->answer, "%d", &nprocs);
Expand Down Expand Up @@ -283,7 +295,7 @@ int main(int argc, char *argv[])
if (param.table->answer)
print_stats_table(stats);
else
print_stats(stats);
print_stats(stats, format);

/* release memory */
free_univar_stat_struct(stats);
Expand Down
14 changes: 13 additions & 1 deletion raster/r.univar/r3.univar_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ void set_params(void)
param.table->description =
_("Table output format instead of standard output format");

param.format = G_define_standard_option(G_OPT_F_FORMAT);
param.format->guisection = _("Print");

return;
}

Expand All @@ -91,6 +94,8 @@ int main(int argc, char *argv[])

struct GModule *module;

enum OutputFormat format;

G_gisinit(argv[0]);

module = G_define_module();
Expand Down Expand Up @@ -129,6 +134,13 @@ int main(int argc, char *argv[])
}
}

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

/* table field separator */
zone_info.sep = G_option_to_separator(param.separator);

Expand Down Expand Up @@ -318,7 +330,7 @@ int main(int argc, char *argv[])
if (param.table->answer)
print_stats_table(stats);
else
print_stats(stats);
print_stats(stats, format);

/* release memory */
free_univar_stat_struct(stats);
Expand Down
149 changes: 125 additions & 24 deletions raster/r.univar/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*
*/

#include <grass/parson.h>
#include "globals.h"

/* *************************************************************** */
Expand Down Expand Up @@ -81,8 +82,20 @@ void free_univar_stat_struct(univar_stat *stats)
/* *************************************************************** */
/* **** compute and print univar statistics to stdout ************ */
/* *************************************************************** */
int print_stats(univar_stat *stats)
int print_stats(univar_stat *stats, enum OutputFormat format)
{
JSON_Value *root_value, *zone_value;
JSON_Array *root_array;
JSON_Object *zone_object;

if (format == JSON) {
root_value = json_value_init_array();
if (root_value == NULL) {
G_fatal_error(_("Failed to initialize JSON array. Out of memory?"));
}
root_array = json_array(root_value);
}

int z, n_zones = zone_info.n_zones;

if (n_zones == 0)
Expand Down Expand Up @@ -117,7 +130,7 @@ int print_stats(univar_stat *stats)
sprintf(sum_str, "%.15g", stats[z].sum);
G_trim_decimal(sum_str);

if (!param.shell_style->answer) {
if (!param.shell_style->answer && format == PLAIN) {
if (zone_info.n_zones) {
int z_cat = z + zone_info.min;

Expand All @@ -131,26 +144,61 @@ int print_stats(univar_stat *stats)
fprintf(stdout, "Of the non-null cells:\n----------------------\n");
}

if (param.shell_style->answer) {
if (param.shell_style->answer || format == JSON) {
if (format == JSON) {
zone_value = json_value_init_object();
zone_object = json_object(zone_value);
}
if (zone_info.n_zones) {
int z_cat = z + zone_info.min;

fprintf(stdout, "zone=%d;%s\n", z_cat,
switch (format) {
case PLAIN:
fprintf(stdout, "zone=%d;%s\n", z_cat,
Rast_get_c_cat(&z_cat, &(zone_info.cats)));
break;
case JSON:
json_object_set_number(zone_object, "zone_number", z_cat);
json_object_set_string(
zone_object, "zone_category",
Rast_get_c_cat(&z_cat, &(zone_info.cats)));
break;
}
}
switch (format) {
case PLAIN:
fprintf(stdout, "n=%lu\n", stats[z].n);
fprintf(stdout, "null_cells=%lu\n", stats[z].size - stats[z].n);
fprintf(stdout, "cells=%lu\n", stats[z].size);
fprintf(stdout, "min=%.15g\n", stats[z].min);
fprintf(stdout, "max=%.15g\n", stats[z].max);
fprintf(stdout, "range=%.15g\n", stats[z].max - stats[z].min);
fprintf(stdout, "mean=%.15g\n", mean);
fprintf(stdout, "mean_of_abs=%.15g\n",
stats[z].sum_abs / stats[z].n);
fprintf(stdout, "stddev=%.15g\n", stdev);
fprintf(stdout, "variance=%.15g\n", variance);
fprintf(stdout, "coeff_var=%.15g\n", var_coef);
fprintf(stdout, "sum=%s\n", sum_str);
break;
case JSON:
json_object_set_number(zone_object, "n", stats[z].n);
json_object_set_number(zone_object, "null_cells",
stats[z].size - stats[z].n);
json_object_set_number(zone_object, "cells", stats[z].size);
json_object_set_number(zone_object, "min", stats[z].min);
json_object_set_number(zone_object, "max", stats[z].max);
json_object_set_number(zone_object, "range",
stats[z].max - stats[z].min);
json_object_set_number(zone_object, "mean", mean);
json_object_set_number(zone_object, "mean_of_abs",
stats[z].sum_abs / stats[z].n);
json_object_set_number(zone_object, "stddev", stdev);
json_object_set_number(zone_object, "variance", variance);
json_object_set_number(zone_object, "coeff_var", var_coef);
json_object_set_number(zone_object, "sum", stats[z].sum);
break;
}
fprintf(stdout, "n=%lu\n", stats[z].n);
fprintf(stdout, "null_cells=%lu\n", stats[z].size - stats[z].n);
fprintf(stdout, "cells=%lu\n", stats[z].size);
fprintf(stdout, "min=%.15g\n", stats[z].min);
fprintf(stdout, "max=%.15g\n", stats[z].max);
fprintf(stdout, "range=%.15g\n", stats[z].max - stats[z].min);
fprintf(stdout, "mean=%.15g\n", mean);
fprintf(stdout, "mean_of_abs=%.15g\n",
stats[z].sum_abs / stats[z].n);
fprintf(stdout, "stddev=%.15g\n", stdev);
fprintf(stdout, "variance=%.15g\n", variance);
fprintf(stdout, "coeff_var=%.15g\n", var_coef);
fprintf(stdout, "sum=%s\n", sum_str);
}
else {
fprintf(stdout, "n: %lu\n", stats[z].n);
Expand Down Expand Up @@ -244,17 +292,57 @@ int print_stats(univar_stat *stats)
}
}

if (param.shell_style->answer) {
fprintf(stdout, "first_quartile=%g\n", quartile_25);
fprintf(stdout, "median=%g\n", median);
fprintf(stdout, "third_quartile=%g\n", quartile_75);
if (param.shell_style->answer || format == JSON) {
switch (format) {
case PLAIN:
fprintf(stdout, "first_quartile=%g\n", quartile_25);
fprintf(stdout, "median=%g\n", median);
fprintf(stdout, "third_quartile=%g\n", quartile_75);
break;
case JSON:
json_object_set_number(zone_object, "first_quartile",
quartile_25);
json_object_set_number(zone_object, "median", median);
json_object_set_number(zone_object, "third_quartile",
quartile_75);
break;
}

JSON_Value *percentiles_array_value, *percentile_value;
JSON_Array *percentiles_array;
JSON_Object *percentile_object;

if (format == JSON) {
percentiles_array_value = json_value_init_array();
percentiles_array = json_array(percentiles_array_value);
}

for (i = 0; i < stats[z].n_perc; i++) {
char buf[24];

sprintf(buf, "%.15g", stats[z].perc[i]);
snprintf(buf, sizeof(buf), "%.15g", stats[z].perc[i]);
G_strchg(buf, '.', '_');
fprintf(stdout, "percentile_%s=%g\n", buf,
quartile_perc[i]);
switch (format) {
case PLAIN:
fprintf(stdout, "percentile_%s=%g\n", buf,
quartile_perc[i]);
break;
case JSON:
percentile_value = json_value_init_object();
percentile_object = json_object(percentile_value);
json_object_set_number(percentile_object, "percentile",
stats[z].perc[i]);
json_object_set_number(percentile_object, "value",
quartile_perc[i]);
json_array_append_value(percentiles_array,
percentile_value);
break;
}
}

if (format == JSON) {
json_object_set_value(zone_object, "percentiles",
percentiles_array_value);
}
}
else {
Expand Down Expand Up @@ -301,6 +389,19 @@ int print_stats(univar_stat *stats)
* above with zone */
/* if (!(param.shell_style->answer))
G_message("\n"); */
if (format == JSON) {
json_array_append_value(root_array, zone_value);
}
}

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);
}

return 1;
Expand Down
Loading
Loading