From 9ae37264ed8d22e9e9727354f7261e5936a1fd9d Mon Sep 17 00:00:00 2001 From: Kriti Birda Date: Fri, 31 May 2024 15:39:56 +0530 Subject: [PATCH] Add JSON support to v.info module Use parson to add json output format support to the v.info module. The module has various flags to control the fields being output in case of plain format. The current prototype ignores the flags and always outputs all data for the JSON output. --- vector/v.info/Makefile | 2 +- vector/v.info/local_proto.h | 11 +- vector/v.info/main.c | 38 +++- vector/v.info/parse.c | 12 +- vector/v.info/print.c | 313 +++++++++++++++++++------- vector/v.info/testsuite/test_vinfo.py | 51 +++++ 6 files changed, 328 insertions(+), 99 deletions(-) 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..031dda7e6ce 100644 --- a/vector/v.info/local_proto.h +++ b/vector/v.info/local_proto.h @@ -1,20 +1,23 @@ #include +#include #define SHELL_NO 0x00 #define SHELL_BASIC 0x02 #define SHELL_REGION 0x04 #define SHELL_TOPO 0x08 +enum OutputFormat { PLAIN, 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..6066680fcbd 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,13 @@ 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); + json_set_float_serialization_format("%lf"); + } /* try to open head-only on level 2 */ if (Vect_open_old_head2(&Map, input_opt, "", field_opt) < 2) { @@ -82,19 +93,30 @@ 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 = 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); + } + Vect_close(&Map); return (EXIT_SUCCESS); diff --git a/vector/v.info/parse.c b/vector/v.info/parse.c index 8d439535443..7d59ed922eb 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,9 @@ 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->guisection = _("Print"); + if (G_parser(argc, argv)) exit(EXIT_FAILURE); @@ -56,4 +59,9 @@ 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, "json") == 0) + *format_ptr = JSON; + else + *format_ptr = PLAIN; } diff --git a/vector/v.info/print.c b/vector/v.info/print.c index 0830006a777..459024aa14e 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,7 @@ 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 +48,33 @@ 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: + 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 +92,72 @@ 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); + switch (format) { + case PLAIN: + fprintf(stdout, "nodes=%d\n", Vect_get_num_nodes(Map)); + fflush(stdout); - fprintf(stdout, "boundaries=%d\n", - Vect_get_num_primitives(Map, GV_BOUNDARY)); - fflush(stdout); + fprintf(stdout, "points=%d\n", Vect_get_num_primitives(Map, GV_POINT)); + fflush(stdout); - fprintf(stdout, "centroids=%d\n", - Vect_get_num_primitives(Map, GV_CENTROID)); - fflush(stdout); + fprintf(stdout, "lines=%d\n", Vect_get_num_primitives(Map, GV_LINE)); + fflush(stdout); - fprintf(stdout, "areas=%d\n", Vect_get_num_areas(Map)); - fflush(stdout); + fprintf(stdout, "boundaries=%d\n", + Vect_get_num_primitives(Map, GV_BOUNDARY)); + fflush(stdout); - fprintf(stdout, "islands=%d\n", Vect_get_num_islands(Map)); - fflush(stdout); + fprintf(stdout, "centroids=%d\n", + Vect_get_num_primitives(Map, GV_CENTROID)); + fflush(stdout); - if (with_z) { - fprintf(stdout, "faces=%d\n", Vect_get_num_primitives(Map, GV_FACE)); + fprintf(stdout, "areas=%d\n", Vect_get_num_areas(Map)); fflush(stdout); - fprintf(stdout, "kernels=%d\n", - Vect_get_num_primitives(Map, GV_KERNEL)); + fprintf(stdout, "islands=%d\n", Vect_get_num_islands(Map)); fflush(stdout); - fprintf(stdout, "volumes=%d\n", - Vect_get_num_primitives(Map, GV_VOLUME)); + 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, "holes=%d\n", Vect_get_num_holes(Map)); + fprintf(stdout, "map3d=%d\n", Vect_is_3d(Map) ? 1 : 0); fflush(stdout); - } - fprintf(stdout, "primitives=%ld\n", nprimitives); - 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_number(root_object, "map3d", Vect_is_3d(Map) ? 1 : 0); + } - fprintf(stdout, "map3d=%d\n", Vect_is_3d(Map) ? 1 : 0); - fflush(stdout); } void print_columns(struct Map_info *Map, const char *input_opt, @@ -174,7 +211,7 @@ 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 +234,73 @@ 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)); + switch (format) { + case PLAIN: + 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)); + 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, "location", G_location()); + 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_number(root_object, "scale", Vect_get_scale(Map)); // fixme: 1:scale + 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: + 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: + 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: + 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: + // fixme: add format=%,%s + 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 +309,113 @@ 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: + 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: + // fixme: add format=%,%s + 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: + 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: + 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: + 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: + 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: + 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: + 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..aa2e80583a3 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,53 @@ 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.000000, + "organization": "", + "timestamp": "15 Jan 1994", + "format": "native", + "level": 2.000000, + "num_dblinks": 1.000000, + "attribute_layer_number": 1.000000, + "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.000000, + "comment": "", + "nodes": 0.000000, + "points": 5.000000, + "lines": 0.000000, + "boundaries": 0.000000, + "centroids": 0.000000, + "areas": 0.000000, + "islands": 0.000000, + "faces": 0.000000, + "kernels": 0.000000, + "volumes": 0.000000, + "holes": 0.000000, + "primitives": 5.000000, + "map3d": 1.000000 + } + 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 = ["location", "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(