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.category: add JSON support #4020

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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.category/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ MODULE_TOPDIR = ../..

PGM = v.category

LIBES = $(VECTORLIB) $(GISLIB)
LIBES = $(VECTORLIB) $(GISLIB) $(PARSONLIB)
DEPENDENCIES = $(VECTORDEP) $(GISDEP)
EXTRA_INC = $(VECT_INC)
EXTRA_CFLAGS = $(VECT_CFLAGS)
Expand Down
75 changes: 71 additions & 4 deletions vector/v.category/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <grass/glocale.h>
#include <grass/gis.h>
#include <grass/vector.h>
#include <grass/parson.h>

#define O_ADD 1
#define O_DEL 2
Expand Down Expand Up @@ -50,6 +51,25 @@ typedef struct {
int min[FRTYPES], max[FRTYPES];
} FREPORT;

enum OutputFormat { PLAIN, SHELL, JSON };

void format_json_fr(FREPORT *freport, int fr_type, char *name,
JSON_Array *array)
{
JSON_Object *object;
JSON_Value *value;
if (freport->count[fr_type] > 0) {
value = json_value_init_object();
object = json_object(value);
json_object_set_string(object, "type", name);
json_object_set_number(object, "field", freport->field);
json_object_set_number(object, "count", freport->count[fr_type]);
json_object_set_number(object, "min", freport->min[fr_type]);
json_object_set_number(object, "max", freport->max[fr_type]);
json_array_append_value(array, value);
}
}

int main(int argc, char *argv[])
{
struct Map_info In, Out;
Expand All @@ -64,12 +84,16 @@ int main(int argc, char *argv[])
int cat, ocat, scat, *fields, nfields, field;
struct GModule *module;
struct Option *in_opt, *out_opt, *option_opt, *type_opt;
struct Option *cat_opt, *field_opt, *step_opt, *id_opt;
struct Option *cat_opt, *field_opt, *step_opt, *id_opt, *format_opt;
struct Flag *shell, *notab;
FREPORT **freps;
int nfreps, rtype, fld;
char *desc;

enum OutputFormat format;
JSON_Array *root_array;
JSON_Value *root_value;

module = G_define_module();
G_add_keyword(_("vector"));
G_add_keyword(_("category"));
Expand Down Expand Up @@ -137,6 +161,13 @@ int main(int argc, char *argv[])
step_opt->answer = "1";
step_opt->description = _("Category increment");

format_opt = G_define_standard_option(G_OPT_F_FORMAT);
format_opt->options = "plain,shell,json";
format_opt->descriptions = _("plain;Human readable text output;"
"shell;shell script style text output;"
"json;JSON (JavaScript Object Notation);");
format_opt->guisection = _("Print");

shell = G_define_flag();
shell->key = 'g';
shell->label = _("Shell script style, currently only for report");
Expand Down Expand Up @@ -231,6 +262,21 @@ int main(int argc, char *argv[])
Clist = NULL;
}

if (strcmp(format_opt->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 if ((strcmp(format_opt->answer, "shell") == 0) || shell->answer) {
format = SHELL;
}
else {
format = PLAIN;
}

if ((option != O_REP) && (option != O_PRN) && (option != O_LYR)) {
if (out_opt->answer == NULL)
G_fatal_error(_("Output vector wasn't entered"));
Expand Down Expand Up @@ -627,7 +673,8 @@ int main(int argc, char *argv[])
}
}
for (i = 0; i < nfreps; i++) {
if (shell->answer) {
switch (format) {
case SHELL:
if (freps[i]->count[FR_POINT] > 0)
fprintf(stdout, "%d point %d %d %d\n", freps[i]->field,
freps[i]->count[FR_POINT],
Expand Down Expand Up @@ -690,8 +737,8 @@ int main(int argc, char *argv[])
freps[i]->count[FR_ALL],
(freps[i]->min[FR_ALL] < 0 ? 0 : freps[i]->min[FR_ALL]),
freps[i]->max[FR_ALL]);
}
else {
break;
case PLAIN:
if (freps[i]->table != NULL) {
fprintf(stdout, "%s: %d/%s\n", _("Layer/table"),
freps[i]->field, freps[i]->table);
Expand Down Expand Up @@ -742,7 +789,27 @@ int main(int argc, char *argv[])
freps[i]->count[FR_ALL],
(freps[i]->min[FR_ALL] < 0) ? 0 : freps[i]->min[FR_ALL],
freps[i]->max[FR_ALL]);
break;
case JSON:
format_json_fr(freps[i], FR_POINT, "point", root_array);
format_json_fr(freps[i], FR_LINE, "line", root_array);
format_json_fr(freps[i], FR_BOUNDARY, "boundary", root_array);
format_json_fr(freps[i], FR_CENTROID, "centroid", root_array);
format_json_fr(freps[i], FR_AREA, "area", root_array);
format_json_fr(freps[i], FR_FACE, "face", root_array);
format_json_fr(freps[i], FR_ALL, "all", root_array);
break;
}
}
if (format == JSON) {
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);
}
break;

Expand Down
23 changes: 23 additions & 0 deletions vector/v.category/testsuite/test_v_category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import json

from grass.gunittest.case import TestCase
from grass.gunittest.gmodules import call_module


class TestVCategory(TestCase):

def test_d_flag(self):
expected = [
{"type": "point", "field": 1, "count": 10938, "min": 1, "max": 10938},
{"type": "all", "field": 1, "count": 10938, "min": 1, "max": 10938},
]
output = call_module(
"v.category", input="bridges", option="report", format="json"
)
self.assertListEqual(expected, json.loads(output))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option print does not respect the format. Add a test case for option="print", format="json".

It can return an array of category numbers.

{
   "categories": [1,2,3,4,5,..]
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same should be done when the option is set to layers.


if __name__ == "__main__":
from grass.gunittest.main import test

test()
24 changes: 24 additions & 0 deletions vector/v.category/v.category.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,30 @@ <h3>Report vector categories</h3>
all 1379 1 1379
</pre></div>

<h3>Report in JSON format</h3>
<div class="code"><pre>
v.category input=testmap option=report format=json
</pre></div>

<div class="code"><pre>
[
{
"type": "line",
"field": 1,
"count": 1379,
"min": 1,
"max": 1379
},
{
"type": "all",
"field": 1,
"count": 1379,
"min": 1,
"max": 1379
}
]
</pre></div>

<h3>Delete all vector categories in layer 1</h3>

<div class="code"><pre>
Expand Down
Loading