diff --git a/raster/r.category/Makefile b/raster/r.category/Makefile index 74909c5f608..5fd448ab5c5 100644 --- a/raster/r.category/Makefile +++ b/raster/r.category/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = r.category -LIBES = $(RASTERLIB) $(GISLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.category/local_proto.h b/raster/r.category/local_proto.h index 05514fbb9ee..370f98cd406 100644 --- a/raster/r.category/local_proto.h +++ b/raster/r.category/local_proto.h @@ -18,13 +18,18 @@ #ifndef __LOCAL_PROTO_H__ #define __LOCAL_PROTO_H__ +#include <grass/parson.h> + +enum OutputFormat { PLAIN, JSON }; + /* cats.c */ int get_cats(const char *, const char *); int next_cat(long *); /* main.c */ -int print_label(long); -int print_d_label(double); +void print_json(JSON_Value *); +int print_label(long, enum OutputFormat, JSON_Array *); +int print_d_label(double, enum OutputFormat, JSON_Array *); int scan_cats(const char *, long *, long *); int scan_vals(const char *, double *); diff --git a/raster/r.category/main.c b/raster/r.category/main.c index b526ba657e2..d0b945a9064 100644 --- a/raster/r.category/main.c +++ b/raster/r.category/main.c @@ -22,6 +22,7 @@ #include <grass/gis.h> #include <grass/raster.h> #include <grass/glocale.h> +#include <grass/parson.h> #include "local_proto.h" static struct Categories cats; @@ -39,9 +40,13 @@ int main(int argc, char *argv[]) int from_stdin = FALSE; struct GModule *module; + enum OutputFormat format; + JSON_Value *root_value; + JSON_Array *root_array; + struct { struct Option *map, *fs, *cats, *vals, *raster, *file, *fmt_str, - *fmt_coeff; + *fmt_coeff, *format; } parm; G_gisinit(argv[0]); @@ -103,9 +108,25 @@ int main(int argc, char *argv[]) parm.fmt_coeff->description = _("Two pairs of category multiplier and offsets, for $1 and $2"); + parm.format = G_define_standard_option(G_OPT_F_FORMAT); + parm.format->key = "output_format"; + parm.format->guisection = _("Print"); + if (G_parser(argc, argv)) exit(EXIT_FAILURE); + if (strcmp(parm.format->answer, "json") == 0) { + 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); + } + else { + format = PLAIN; + } + name = parm.map->answer; fs = G_option_to_separator(parm.fs); @@ -282,7 +303,10 @@ int main(int argc, char *argv[]) if (map_type == CELL_TYPE) { get_cats(name, mapset); while (next_cat(&x)) - print_label(x); + print_label(x, format, root_array); + if (format == JSON) { + print_json(root_value); + } exit(EXIT_SUCCESS); } } @@ -300,7 +324,10 @@ int main(int argc, char *argv[]) for (i = 0; parm.cats->answers[i]; i++) { scan_cats(parm.cats->answers[i], &x, &y); while (x <= y) - print_label(x++); + print_label(x++, format, root_array); + } + if (format == JSON) { + print_json(root_value); } exit(EXIT_SUCCESS); } @@ -315,31 +342,76 @@ int main(int argc, char *argv[]) } for (i = 0; parm.vals->answers[i]; i++) { scan_vals(parm.vals->answers[i], &dx); - print_d_label(dx); + print_d_label(dx, format, root_array); + } + + if (format == JSON) { + print_json(root_value); } + exit(EXIT_SUCCESS); } -int print_label(long x) +void print_json(JSON_Value *root_value) +{ + char *serialized_string = NULL; + 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); +} + +int print_label(long x, enum OutputFormat format, JSON_Array *root_array) { char *label; + JSON_Value *category_value; + JSON_Object *category; G_squeeze(label = Rast_get_c_cat((CELL *)&x, &cats)); - fprintf(stdout, "%ld%s%s\n", x, fs, label); + + switch (format) { + case PLAIN: + fprintf(stdout, "%ld%s%s\n", x, fs, label); + break; + case JSON: + category_value = json_value_init_object(); + category = json_object(category_value); + json_object_set_number(category, "category", x); + json_object_set_string(category, "description", label); + json_array_append_value(root_array, category_value); + break; + } return 0; } -int print_d_label(double x) +int print_d_label(double x, enum OutputFormat format, JSON_Array *root_array) { char *label, tmp[40]; DCELL dtmp; + JSON_Value *category_value; + JSON_Object *category; dtmp = x; G_squeeze(label = Rast_get_d_cat(&dtmp, &cats)); - sprintf(tmp, "%.10f", x); - G_trim_decimal(tmp); - fprintf(stdout, "%s%s%s\n", tmp, fs, label); + + switch (format) { + case PLAIN: + sprintf(tmp, "%.10f", x); + G_trim_decimal(tmp); + fprintf(stdout, "%s%s%s\n", tmp, fs, label); + break; + case JSON: + category_value = json_value_init_object(); + category = json_object(category_value); + json_object_set_number(category, "category", x); + json_object_set_string(category, "description", label); + json_array_append_value(root_array, category_value); + break; + } return 0; } diff --git a/raster/r.category/r.category.html b/raster/r.category/r.category.html index 9a10e63779c..4dba832a98c 100644 --- a/raster/r.category/r.category.html +++ b/raster/r.category/r.category.html @@ -147,6 +147,26 @@ <h3>Printing categories</h3> as the character separating the category values from the category values in the output. +<p> +<div class="code"><pre> +r.category map=landclass96 cats=3,4 output_format=json +</pre></div> + +generates the following JSON output: + +<div class="code"><pre> +[ + { + "category": 3, + "description": "herbaceous" + }, + { + "category": 4, + "description": "shrubland" + } +] +</pre></div> + <h3>Adding categories</h3> Example for defining new category labels, using a colon as separator: diff --git a/raster/r.category/test_rcategory_doctest.txt b/raster/r.category/test_rcategory_doctest.txt index 9276c3e9e62..b8424bbb253 100644 --- a/raster/r.category/test_rcategory_doctest.txt +++ b/raster/r.category/test_rcategory_doctest.txt @@ -224,6 +224,22 @@ Some of these commands should not work and return 1. <BLANKLINE> +JSON Output +=========== +>>> print(read_command('r.category', map='test', output_format='json')) +[ + { + "category": 1, + "description": "trees, very green" + }, + { + "category": 2, + "description": "water, very deep" + } +] +<BLANKLINE> + + Clean the results =================