Skip to content

Commit

Permalink
r.univar: add JSON support (OSGeo#3783)
Browse files Browse the repository at this point in the history
* Add JSON support to r.univar

Use parson to add json output format support to the r.univar module.

* Apply suggestions from code review

---------

Co-authored-by: Nicklas Larsson <n_larsson@yahoo.com>
  • Loading branch information
2 people authored and a0x8o committed Jul 23, 2024
1 parent 3cc60fd commit 4fd6484
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 33 deletions.
5 changes: 5 additions & 0 deletions raster/r.univar/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ MODULE_TOPDIR = ../..
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
<<<<<<< HEAD
<<<<<<< HEAD
Expand Down Expand Up @@ -51,6 +52,10 @@ MODULE_TOPDIR = ../..
>>>>>>> ebf041644a (r.horizon manual - fix typo (#2794))
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)
>>>>>>> 71ed433fe3 (r.univar: add JSON support (#3783))
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 @@ -485,6 +485,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 @@ -159,6 +159,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 @@ -336,6 +339,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 @@ -423,6 +428,13 @@ int main(int argc, char *argv[])
>>>>>>> 8f5c741ca6 (wxpyimgview: explicit conversion to int (#2704))
}

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 @@ -1030,7 +1042,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 @@ -646,7 +658,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 @@ -351,8 +352,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 @@ -610,7 +623,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 @@ -624,26 +637,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 @@ -737,17 +785,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 @@ -1363,6 +1451,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

0 comments on commit 4fd6484

Please sign in to comment.