diff --git a/lib/gis/testsuite/test_parser_json.py b/lib/gis/testsuite/test_parser_json.py index b325de2314c..918fb453d04 100644 --- a/lib/gis/testsuite/test_parser_json.py +++ b/lib/gis/testsuite/test_parser_json.py @@ -97,13 +97,15 @@ def test_v_info(self): inputs = [ {"param": "map", "value": "hospitals@PERMANENT"}, {"param": "layer", "value": "1"}, + {"param": "format", "value": "plain"}, ] stdout, stderr = subprocess.Popen(args, stdout=subprocess.PIPE).communicate() print(stdout) json_code = json.loads(decode(stdout)) + print(json_code) self.assertEqual(json_code["module"], "v.info") - self.assertEqual(len(json_code["inputs"]), 2) + self.assertEqual(len(json_code["inputs"]), 3) self.assertEqual(json_code["inputs"], inputs) diff --git a/vector/v.info/Makefile b/vector/v.info/Makefile index e7bb7e6eff6..6791e2513c7 100644 --- a/vector/v.info/Makefile +++ b/vector/v.info/Makefile @@ -3,7 +3,7 @@ MODULE_TOPDIR = ../.. PGM = v.info -LIBES = $(VECTORLIB) $(DIG2LIB) $(DBMILIB) $(GISLIB) +LIBES = $(VECTORLIB) $(DIG2LIB) $(DBMILIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(VECTORDEP) $(DIG2DEP) $(DBMIDEP) $(GISDEP) EXTRA_INC = $(VECT_INC) EXTRA_CFLAGS = $(VECT_CFLAGS) diff --git a/vector/v.info/local_proto.h b/vector/v.info/local_proto.h index 8d16b41bbe1..1ed32468562 100644 --- a/vector/v.info/local_proto.h +++ b/vector/v.info/local_proto.h @@ -1,20 +1,25 @@ #include +#include #define SHELL_NO 0x00 #define SHELL_BASIC 0x02 #define SHELL_REGION 0x04 #define SHELL_TOPO 0x08 +enum OutputFormat { PLAIN, SHELL, JSON }; + /* level1.c */ int level_one_info(struct Map_info *); /* parse.c */ -void parse_args(int, char **, char **, char **, int *, int *, int *); +void parse_args(int, char **, char **, char **, int *, int *, int *, + enum OutputFormat *); /* print.c */ void format_double(double, char *); -void print_region(struct Map_info *); -void print_topo(struct Map_info *); +void print_region(struct Map_info *, enum OutputFormat, JSON_Object *); +void print_topo(struct Map_info *, enum OutputFormat, JSON_Object *); void print_columns(struct Map_info *, const char *, const char *); void print_info(struct Map_info *); -void print_shell(struct Map_info *, const char *); +void print_shell(struct Map_info *, const char *, enum OutputFormat, + JSON_Object *); diff --git a/vector/v.info/main.c b/vector/v.info/main.c index 4506b2b7bf6..6f4aa9a8fcf 100644 --- a/vector/v.info/main.c +++ b/vector/v.info/main.c @@ -29,6 +29,11 @@ int main(int argc, char *argv[]) char *input_opt, *field_opt; int hist_flag, col_flag, shell_flag; + enum OutputFormat format; + + JSON_Value *root_value; + JSON_Object *root_object; + struct Map_info Map; G_gisinit(argv[0]); @@ -47,7 +52,12 @@ int main(int argc, char *argv[]) G_debug(1, "LFS is %s", sizeof(off_t) == 8 ? "available" : "not available"); parse_args(argc, argv, &input_opt, &field_opt, &hist_flag, &col_flag, - &shell_flag); + &shell_flag, &format); + + if (format == JSON) { + root_value = json_value_init_object(); + root_object = json_value_get_object(root_value); + } /* try to open head-only on level 2 */ if (Vect_open_old_head2(&Map, input_opt, "", field_opt) < 2) { @@ -82,19 +92,29 @@ int main(int argc, char *argv[]) return (EXIT_SUCCESS); } - if (shell_flag & SHELL_BASIC) { - print_shell(&Map, field_opt); + if ((shell_flag & SHELL_BASIC) || format == JSON) { + print_shell(&Map, field_opt, format, root_object); } - if (shell_flag & SHELL_REGION) { - print_region(&Map); + if ((shell_flag & SHELL_REGION) || format == JSON) { + print_region(&Map, format, root_object); } - if (shell_flag & SHELL_TOPO) { - print_topo(&Map); + if ((shell_flag & SHELL_TOPO) || format == JSON) { + print_topo(&Map, format, root_object); } - if (shell_flag == 0) { + if (shell_flag == 0 && format == PLAIN) { print_info(&Map); } + 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); + } + Vect_close(&Map); return (EXIT_SUCCESS); diff --git a/vector/v.info/parse.c b/vector/v.info/parse.c index 8d439535443..b63b7a64055 100644 --- a/vector/v.info/parse.c +++ b/vector/v.info/parse.c @@ -6,9 +6,9 @@ #include "local_proto.h" void parse_args(int argc, char **argv, char **input, char **field, int *history, - int *columns, int *shell) + int *columns, int *shell, enum OutputFormat *format_ptr) { - struct Option *input_opt, *field_opt; + struct Option *input_opt, *field_opt, *format_opt; struct Flag *hist_flag, *col_flag, *shell_flag, *region_flag, *topo_flag; input_opt = G_define_standard_option(G_OPT_V_MAP); @@ -42,6 +42,13 @@ void parse_args(int argc, char **argv, char **input, char **field, int *history, topo_flag->description = _("Print topology info in shell script style"); topo_flag->guisection = _("Print"); + 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"); + if (G_parser(argc, argv)) exit(EXIT_FAILURE); @@ -56,4 +63,27 @@ void parse_args(int argc, char **argv, char **input, char **field, int *history, *shell |= SHELL_REGION; if (topo_flag->answer) *shell |= SHELL_TOPO; + + if (strcmp(format_opt->answer, "plain") == 0) { + // if shell flags are specified and format=PLAIN (default), + // print in shell script format + if (*shell != 0) { + *format_ptr = SHELL; + } + else { + *format_ptr = PLAIN; + } + } + else if (strcmp(format_opt->answer, "json") == 0) + *format_ptr = JSON; + else { + *format_ptr = SHELL; + // if shell flags are specified with format=shell, obey them + // if only format=shell is specified, print all info + if (*shell == 0) { + *shell |= SHELL_BASIC; + *shell |= SHELL_REGION; + *shell |= SHELL_TOPO; + } + } } diff --git a/vector/v.info/print.c b/vector/v.info/print.c index 0830006a777..6c36d6391f6 100644 --- a/vector/v.info/print.c +++ b/vector/v.info/print.c @@ -5,6 +5,8 @@ #include #include +#include + #include "local_proto.h" #define printline(x) G_faprintf(stdout, " | %-74.74s |\n", x) @@ -38,7 +40,8 @@ static char *format_zone(int zone_num) return zone_str; } -void print_region(struct Map_info *Map) +void print_region(struct Map_info *Map, enum OutputFormat format, + JSON_Object *root_object) { char tmp1[1024], tmp2[1024]; @@ -46,20 +49,36 @@ void print_region(struct Map_info *Map) /* print the spatial extent as double values */ Vect_get_map_box(Map, &box); - G_format_northing(box.N, tmp1, -1); - G_format_northing(box.S, tmp2, -1); - fprintf(stdout, "north=%s\n", tmp1); - fprintf(stdout, "south=%s\n", tmp2); - - G_format_easting(box.E, tmp1, -1); - G_format_easting(box.W, tmp2, -1); - fprintf(stdout, "east=%s\n", tmp1); - fprintf(stdout, "west=%s\n", tmp2); - fprintf(stdout, "top=%f\n", box.T); - fprintf(stdout, "bottom=%f\n", box.B); + + switch (format) { + case PLAIN: + break; + case SHELL: + G_format_northing(box.N, tmp1, -1); + G_format_northing(box.S, tmp2, -1); + fprintf(stdout, "north=%s\n", tmp1); + fprintf(stdout, "south=%s\n", tmp2); + + G_format_easting(box.E, tmp1, -1); + G_format_easting(box.W, tmp2, -1); + fprintf(stdout, "east=%s\n", tmp1); + fprintf(stdout, "west=%s\n", tmp2); + fprintf(stdout, "top=%f\n", box.T); + fprintf(stdout, "bottom=%f\n", box.B); + break; + case JSON: + json_object_set_number(root_object, "north", box.N); + json_object_set_number(root_object, "south", box.S); + json_object_set_number(root_object, "east", box.E); + json_object_set_number(root_object, "west", box.W); + json_object_set_number(root_object, "top", box.T); + json_object_set_number(root_object, "bottom", box.B); + break; + } } -void print_topo(struct Map_info *Map) +void print_topo(struct Map_info *Map, enum OutputFormat format, + JSON_Object *root_object) { int with_z; long nprimitives; @@ -77,50 +96,83 @@ void print_topo(struct Map_info *Map) nprimitives += Vect_get_num_primitives(Map, GV_KERNEL); } - fprintf(stdout, "nodes=%d\n", Vect_get_num_nodes(Map)); - fflush(stdout); - - fprintf(stdout, "points=%d\n", Vect_get_num_primitives(Map, GV_POINT)); - fflush(stdout); - - fprintf(stdout, "lines=%d\n", Vect_get_num_primitives(Map, GV_LINE)); - fflush(stdout); - - fprintf(stdout, "boundaries=%d\n", - Vect_get_num_primitives(Map, GV_BOUNDARY)); - fflush(stdout); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "nodes=%d\n", Vect_get_num_nodes(Map)); + fflush(stdout); - fprintf(stdout, "centroids=%d\n", - Vect_get_num_primitives(Map, GV_CENTROID)); - fflush(stdout); + fprintf(stdout, "points=%d\n", Vect_get_num_primitives(Map, GV_POINT)); + fflush(stdout); - fprintf(stdout, "areas=%d\n", Vect_get_num_areas(Map)); - fflush(stdout); + fprintf(stdout, "lines=%d\n", Vect_get_num_primitives(Map, GV_LINE)); + fflush(stdout); - fprintf(stdout, "islands=%d\n", Vect_get_num_islands(Map)); - fflush(stdout); + fprintf(stdout, "boundaries=%d\n", + Vect_get_num_primitives(Map, GV_BOUNDARY)); + fflush(stdout); - if (with_z) { - fprintf(stdout, "faces=%d\n", Vect_get_num_primitives(Map, GV_FACE)); + fprintf(stdout, "centroids=%d\n", + Vect_get_num_primitives(Map, GV_CENTROID)); fflush(stdout); - fprintf(stdout, "kernels=%d\n", - Vect_get_num_primitives(Map, GV_KERNEL)); + fprintf(stdout, "areas=%d\n", Vect_get_num_areas(Map)); fflush(stdout); - fprintf(stdout, "volumes=%d\n", - Vect_get_num_primitives(Map, GV_VOLUME)); + fprintf(stdout, "islands=%d\n", Vect_get_num_islands(Map)); fflush(stdout); - fprintf(stdout, "holes=%d\n", Vect_get_num_holes(Map)); + if (with_z) { + fprintf(stdout, "faces=%d\n", + Vect_get_num_primitives(Map, GV_FACE)); + fflush(stdout); + + fprintf(stdout, "kernels=%d\n", + Vect_get_num_primitives(Map, GV_KERNEL)); + fflush(stdout); + + fprintf(stdout, "volumes=%d\n", + Vect_get_num_primitives(Map, GV_VOLUME)); + fflush(stdout); + + fprintf(stdout, "holes=%d\n", Vect_get_num_holes(Map)); + fflush(stdout); + } + + fprintf(stdout, "primitives=%ld\n", nprimitives); fflush(stdout); - } - fprintf(stdout, "primitives=%ld\n", nprimitives); - fflush(stdout); + fprintf(stdout, "map3d=%d\n", Vect_is_3d(Map) ? 1 : 0); + fflush(stdout); - fprintf(stdout, "map3d=%d\n", Vect_is_3d(Map) ? 1 : 0); - fflush(stdout); + break; + case JSON: + json_object_set_number(root_object, "nodes", Vect_get_num_nodes(Map)); + json_object_set_number(root_object, "points", + Vect_get_num_primitives(Map, GV_POINT)); + json_object_set_number(root_object, "lines", + Vect_get_num_primitives(Map, GV_LINE)); + json_object_set_number(root_object, "boundaries", + Vect_get_num_primitives(Map, GV_BOUNDARY)); + json_object_set_number(root_object, "centroids", + Vect_get_num_primitives(Map, GV_CENTROID)); + json_object_set_number(root_object, "areas", Vect_get_num_areas(Map)); + json_object_set_number(root_object, "islands", + Vect_get_num_islands(Map)); + if (with_z) { + json_object_set_number(root_object, "faces", + Vect_get_num_primitives(Map, GV_FACE)); + json_object_set_number(root_object, "kernels", + Vect_get_num_primitives(Map, GV_KERNEL)); + json_object_set_number(root_object, "volumes", + Vect_get_num_primitives(Map, GV_VOLUME)); + json_object_set_number(root_object, "holes", + Vect_get_num_holes(Map)); + } + json_object_set_number(root_object, "primitives", nprimitives); + json_object_set_boolean(root_object, "map3d", Vect_is_3d(Map)); + } } void print_columns(struct Map_info *Map, const char *input_opt, @@ -174,7 +226,8 @@ void print_columns(struct Map_info *Map, const char *input_opt, db_shutdown_driver(driver); } -void print_shell(struct Map_info *Map, const char *field_opt) +void print_shell(struct Map_info *Map, const char *field_opt, + enum OutputFormat format, JSON_Object *root_object) { int map_type; int time_ok, first_time_ok, second_time_ok; @@ -197,31 +250,91 @@ void print_shell(struct Map_info *Map, const char *field_opt) map_type = Vect_maptype(Map); - fprintf(stdout, "name=%s\n", Vect_get_name(Map)); - fprintf(stdout, "mapset=%s\n", Vect_get_mapset(Map)); - fprintf(stdout, "location=%s\n", G_location()); - fprintf(stdout, "project=%s\n", G_location()); - fprintf(stdout, "database=%s\n", G_gisdbase()); - fprintf(stdout, "title=%s\n", Vect_get_map_name(Map)); - fprintf(stdout, "scale=1:%d\n", Vect_get_scale(Map)); - fprintf(stdout, "creator=%s\n", Vect_get_person(Map)); - fprintf(stdout, "organization=%s\n", Vect_get_organization(Map)); - fprintf(stdout, "source_date=%s\n", Vect_get_map_date(Map)); + char scale_tmp[18]; + snprintf(scale_tmp, 18, "1:%d", Vect_get_scale(Map)); + + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "name=%s\n", Vect_get_name(Map)); + fprintf(stdout, "mapset=%s\n", Vect_get_mapset(Map)); + fprintf(stdout, "location=%s\n", G_location()); + fprintf(stdout, "project=%s\n", G_location()); + fprintf(stdout, "database=%s\n", G_gisdbase()); + fprintf(stdout, "title=%s\n", Vect_get_map_name(Map)); + fprintf(stdout, "scale=%s\n", scale_tmp); + fprintf(stdout, "creator=%s\n", Vect_get_person(Map)); + fprintf(stdout, "organization=%s\n", Vect_get_organization(Map)); + fprintf(stdout, "source_date=%s\n", Vect_get_map_date(Map)); + break; + case JSON: + json_object_set_string(root_object, "name", Vect_get_name(Map)); + json_object_set_string(root_object, "mapset", Vect_get_mapset(Map)); + json_object_set_string(root_object, "project", G_location()); + json_object_set_string(root_object, "database", G_gisdbase()); + json_object_set_string(root_object, "title", Vect_get_map_name(Map)); + json_object_set_string(root_object, "scale", scale_tmp); + json_object_set_string(root_object, "creator", Vect_get_person(Map)); + json_object_set_string(root_object, "organization", + Vect_get_organization(Map)); + json_object_set_string(root_object, "source_date", + Vect_get_map_date(Map)); + break; + } + /* This shows the TimeStamp (if present) */ if (time_ok == TRUE && (first_time_ok || second_time_ok)) { G_format_timestamp(&ts, timebuff); - fprintf(stdout, "timestamp=%s\n", timebuff); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "timestamp=%s\n", timebuff); + break; + case JSON: + json_object_set_string(root_object, "timestamp", timebuff); + break; + } } else { - fprintf(stdout, "timestamp=none\n"); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "timestamp=none\n"); + break; + case JSON: + json_object_set_null(root_object, "timestamp"); + break; + } } if (map_type == GV_FORMAT_OGR || map_type == GV_FORMAT_OGR_DIRECT) { - fprintf(stdout, "format=%s,%s\n", Vect_maptype_info(Map), - Vect_get_finfo_format_info(Map)); - fprintf(stdout, "ogr_layer=%s\n", Vect_get_finfo_layer_name(Map)); - fprintf(stdout, "ogr_dsn=%s\n", Vect_get_finfo_dsn_name(Map)); - fprintf(stdout, "feature_type=%s\n", Vect_get_finfo_geometry_type(Map)); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "format=%s,%s\n", Vect_maptype_info(Map), + Vect_get_finfo_format_info(Map)); + fprintf(stdout, "ogr_layer=%s\n", Vect_get_finfo_layer_name(Map)); + fprintf(stdout, "ogr_dsn=%s\n", Vect_get_finfo_dsn_name(Map)); + fprintf(stdout, "feature_type=%s\n", + Vect_get_finfo_geometry_type(Map)); + break; + case JSON: + json_object_set_string(root_object, "format", + Vect_maptype_info(Map)); + json_object_set_string(root_object, "format-detail", + Vect_get_finfo_format_info(Map)); + json_object_set_string(root_object, "ogr_layer", + Vect_get_finfo_layer_name(Map)); + json_object_set_string(root_object, "ogr_dsn", + Vect_get_finfo_dsn_name(Map)); + json_object_set_string(root_object, "feature_type", + Vect_get_finfo_geometry_type(Map)); + break; + } } else if (map_type == GV_FORMAT_POSTGIS) { int topo_format; @@ -230,47 +343,146 @@ void print_shell(struct Map_info *Map, const char *field_opt) finfo = Vect_get_finfo(Map); - fprintf(stdout, "format=%s,%s\n", Vect_maptype_info(Map), - Vect_get_finfo_format_info(Map)); - fprintf(stdout, "pg_table=%s\n", Vect_get_finfo_layer_name(Map)); - fprintf(stdout, "pg_dbname=%s\n", Vect_get_finfo_dsn_name(Map)); - fprintf(stdout, "geometry_column=%s\n", finfo->pg.geom_column); - fprintf(stdout, "feature_type=%s\n", Vect_get_finfo_geometry_type(Map)); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "format=%s,%s\n", Vect_maptype_info(Map), + Vect_get_finfo_format_info(Map)); + fprintf(stdout, "pg_table=%s\n", Vect_get_finfo_layer_name(Map)); + fprintf(stdout, "pg_dbname=%s\n", Vect_get_finfo_dsn_name(Map)); + fprintf(stdout, "geometry_column=%s\n", finfo->pg.geom_column); + fprintf(stdout, "feature_type=%s\n", + Vect_get_finfo_geometry_type(Map)); + break; + case JSON: + json_object_set_string(root_object, "format", + Vect_maptype_info(Map)); + json_object_set_string(root_object, "format-detail", + Vect_get_finfo_format_info(Map)); + json_object_set_string(root_object, "pg_table", + Vect_get_finfo_layer_name(Map)); + json_object_set_string(root_object, "pg_dbname", + Vect_get_finfo_dsn_name(Map)); + json_object_set_string(root_object, "geometry_column", + finfo->pg.geom_column); + json_object_set_string(root_object, "feature_type", + Vect_get_finfo_geometry_type(Map)); + break; + } + topo_format = Vect_get_finfo_topology_info(Map, &toposchema_name, &topogeom_column, NULL); if (topo_format == GV_TOPO_POSTGIS) { - fprintf(stdout, "pg_topo_schema=%s\n", toposchema_name); - fprintf(stdout, "pg_topo_column=%s\n", topogeom_column); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "pg_topo_schema=%s\n", toposchema_name); + fprintf(stdout, "pg_topo_column=%s\n", topogeom_column); + break; + case JSON: + json_object_set_string(root_object, "pg_topo_schema", + toposchema_name); + json_object_set_string(root_object, "pg_topo_column", + topogeom_column); + break; + } } } else { - fprintf(stdout, "format=%s\n", Vect_maptype_info(Map)); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "format=%s\n", Vect_maptype_info(Map)); + break; + case JSON: + json_object_set_string(root_object, "format", + Vect_maptype_info(Map)); + break; + } } - fprintf(stdout, "level=%d\n", Vect_level(Map)); - + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "level=%d\n", Vect_level(Map)); + break; + case JSON: + json_object_set_number(root_object, "level", Vect_level(Map)); + break; + } if (Vect_level(Map) > 0) { - fprintf(stdout, "num_dblinks=%d\n", Vect_get_num_dblinks(Map)); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "num_dblinks=%d\n", Vect_get_num_dblinks(Map)); + break; + case JSON: + json_object_set_number(root_object, "num_dblinks", + Vect_get_num_dblinks(Map)); + break; + } if (Vect_get_num_dblinks(Map) > 0) { fi = Vect_get_field2(Map, field_opt); if (fi != NULL) { - fprintf(stdout, "attribute_layer_number=%i\n", fi->number); - fprintf(stdout, "attribute_layer_name=%s\n", fi->name); - fprintf(stdout, "attribute_database=%s\n", fi->database); - fprintf(stdout, "attribute_database_driver=%s\n", fi->driver); - fprintf(stdout, "attribute_table=%s\n", fi->table); - fprintf(stdout, "attribute_primary_key=%s\n", fi->key); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "attribute_layer_number=%i\n", fi->number); + fprintf(stdout, "attribute_layer_name=%s\n", fi->name); + fprintf(stdout, "attribute_database=%s\n", fi->database); + fprintf(stdout, "attribute_database_driver=%s\n", + fi->driver); + fprintf(stdout, "attribute_table=%s\n", fi->table); + fprintf(stdout, "attribute_primary_key=%s\n", fi->key); + break; + case JSON: + json_object_set_number( + root_object, "attribute_layer_number", fi->number); + json_object_set_string(root_object, "attribute_layer_name", + fi->name); + json_object_set_string(root_object, "attribute_database", + fi->database); + json_object_set_string( + root_object, "attribute_database_driver", fi->driver); + json_object_set_string(root_object, "attribute_table", + fi->table); + json_object_set_string(root_object, "attribute_primary_key", + fi->key); + break; + } } } } - fprintf(stdout, "projection=%s\n", Vect_get_proj_name(Map)); - if (G_projection() == PROJECTION_UTM) { - fprintf(stdout, "zone=%d\n", Vect_get_zone(Map)); + switch (format) { + case PLAIN: + break; + case SHELL: + fprintf(stdout, "projection=%s\n", Vect_get_proj_name(Map)); + if (G_projection() == PROJECTION_UTM) { + fprintf(stdout, "zone=%d\n", Vect_get_zone(Map)); + } + fprintf(stdout, "digitization_threshold=%f\n", Vect_get_thresh(Map)); + fprintf(stdout, "comment=%s\n", Vect_get_comment(Map)); + break; + case JSON: + json_object_set_string(root_object, "projection", + Vect_get_proj_name(Map)); + if (G_projection() == PROJECTION_UTM) { + json_object_set_number(root_object, "zone", Vect_get_zone(Map)); + } + json_object_set_number(root_object, "digitization_threshold", + Vect_get_thresh(Map)); + json_object_set_string(root_object, "comment", Vect_get_comment(Map)); + break; } - fprintf(stdout, "digitization_threshold=%f\n", Vect_get_thresh(Map)); - fprintf(stdout, "comment=%s\n", Vect_get_comment(Map)); } void print_info(struct Map_info *Map) diff --git a/vector/v.info/testsuite/test_vinfo.py b/vector/v.info/testsuite/test_vinfo.py index b163df02327..b5b3aa17b82 100644 --- a/vector/v.info/testsuite/test_vinfo.py +++ b/vector/v.info/testsuite/test_vinfo.py @@ -1,6 +1,10 @@ +import json + from grass.gunittest.case import TestCase from grass.gunittest.main import test +from grass.gunittest.gmodules import SimpleModule + class TestVInfo(TestCase): """Test the shell output of v.info that is not location/mapset or user dependent""" @@ -183,6 +187,65 @@ def test_info_with_db_3d(self): ), ) + def test_json(self): + module = SimpleModule("v.info", map=self.test_vinfo_with_db_3d, format="json") + self.runModule(module) + + expected = { + "name": "test_vinfo_with_db_3d", + "title": "", + "scale": "1:1", + "organization": "", + "timestamp": "15 Jan 1994", + "format": "native", + "level": 2, + "num_dblinks": 1, + "attribute_layer_number": 1, + "attribute_layer_name": "test_vinfo_with_db_3d", + "attribute_database_driver": "sqlite", + "attribute_table": "test_vinfo_with_db_3d", + "attribute_primary_key": "cat", + "projection": "Lambert Conformal Conic", + "digitization_threshold": 0, + "comment": "", + "nodes": 0, + "points": 5, + "lines": 0, + "boundaries": 0, + "centroids": 0, + "areas": 0, + "islands": 0, + "faces": 0, + "kernels": 0, + "volumes": 0, + "holes": 0, + "primitives": 5, + "map3d": 1, + } + result = json.loads(module.outputs.stdout) + + # the following fields vary with the Grass sample data's path + # therefore only check for their presence in the JSON output + # and not exact values + remove_fields = [ + "project", + "database", + "source_date", + "attribute_database", + "top", + "bottom", + "east", + "west", + "north", + "south", + "creator", + "mapset", + ] + for field in remove_fields: + self.assertIn(field, result) + result.pop(field) + self.assertDictEqual(expected, result) + def test_database_table(self): """Test the database table column and type of the two vector maps with attribute data""" self.assertModuleKeyValue( diff --git a/vector/v.info/v.info.html b/vector/v.info/v.info.html index 1facf172dc9..05e7903ac2d 100644 --- a/vector/v.info/v.info.html +++ b/vector/v.info/v.info.html @@ -136,6 +136,49 @@

Basic metadata information in shell script style

bottom=0.000000 +

Output in JSON format

+
+{
+    "name": "geology",
+    "mapset": "PERMANENT",
+    "project": "nc_spm_08_grass7",
+    "database": "\/grassdata",
+    "title": "North Carolina geology map (polygon map)",
+    "scale": 1,
+    "creator": "helena",
+    "organization": "NC OneMap",
+    "source_date": "Mon Nov  6 15:48:53 2006",
+    "timestamp": null,
+    "format": "native",
+    "level": 2,
+    "num_dblinks": 1,
+    "attribute_layer_number": 1,
+    "attribute_layer_name": "geology",
+    "attribute_database": "\/grassdata\/nc_spm_08_grass7\/PERMANENT\/sqlite\/sqlite.db",
+    "attribute_database_driver": "sqlite",
+    "attribute_table": "geology",
+    "attribute_primary_key": "cat",
+    "projection": "Lambert Conformal Conic",
+    "digitization_threshold": 0,
+    "comment": "",
+    "north": 318117.43741634465,
+    "south": 10875.827232091688,
+    "east": 930172.31282271142,
+    "west": 123971.19498978264,
+    "top": 0,
+    "bottom": 0,
+    "nodes": 2724,
+    "points": 0,
+    "lines": 0,
+    "boundaries": 3649,
+    "centroids": 1832,
+    "areas": 1832,
+    "islands": 907,
+    "primitives": 5481,
+    "map3d": false
+}
+
+

PYTHON

See Python @@ -151,6 +194,21 @@

PYTHON

gcore.vector_info_topo('geology') # for `v.info shell=topo` +Here is an example of how the JSON output format can be used to integrate Grass with other python libraries easily. +
+import grass.script as gs
+import pandas as pd
+
+# Run v.info command
+busstops = gs.run_command("v.info", map="busstopsall", format="json")
+
+# Load data into dataframe
+df = pd.DataFrame([busstops])
+
+# Display the DataFrame
+print(df)
+
+

SEE ALSO