From c1f55e303d175a8ad02d39cf24076d7a96026ce2 Mon Sep 17 00:00:00 2001 From: Kriti Birda <164247895+kritibirda26@users.noreply.github.com> Date: Sat, 20 Jul 2024 03:23:19 +0530 Subject: [PATCH] r.category: add JSON support (#4018) * r.category: add JSON support * add test and docs * address PR feedback --- raster/r.category/Makefile | 2 +- raster/r.category/local_proto.h | 9 +- raster/r.category/main.c | 92 +++++++++++++++++--- raster/r.category/r.category.html | 20 +++++ raster/r.category/test_rcategory_doctest.txt | 16 ++++ 5 files changed, 126 insertions(+), 13 deletions(-) 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 + +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 #include #include +#include #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 @@

Printing categories

as the character separating the category values from the category values in the output. +

+

+r.category map=landclass96 cats=3,4 output_format=json
+
+ +generates the following JSON output: + +
+[
+    {
+        "category": 3,
+        "description": "herbaceous"
+    },
+    {
+        "category": 4,
+        "description": "shrubland"
+    }
+]
+
+

Adding categories

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. +JSON Output +=========== +>>> print(read_command('r.category', map='test', output_format='json')) +[ + { + "category": 1, + "description": "trees, very green" + }, + { + "category": 2, + "description": "water, very deep" + } +] + + + Clean the results =================