GRASS GIS 7 Programmer's Manual  7.8.2(2019)-exported
parser_json.c
Go to the documentation of this file.
1 /*!
2  \file lib/gis/parser_json.c
3 
4  \brief GIS Library - converts the command line arguments into actinia JSON process
5  chain building blocks
6 
7  (C) 2018 by the GRASS Development Team
8 
9  This program is free software under the GNU General Public License
10  (>=v2). Read the file COPYING that comes with GRASS for details.
11 
12  \author Soeren Gebbert
13 */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <grass/gis.h>
18 
19 #include "parser_local_proto.h"
20 
21 void check_create_import_opts(struct Option *, char *, FILE *);
22 void check_create_export_opts(struct Option *, char *, FILE *);
23 char *check_mapset_in_layer_name(char *, int);
24 
25 /*!
26  \brief This function generates actinia JSON process chain building blocks
27  from the command line arguments that can be used in the actinia processing API.
28 
29  The following commands will create according JSON output:
30 
31  r.slope.aspect elevation="elevation+https://storage.googleapis.com/graas-geodata/elev_ned_30m.tif" slope="slope+GTiff" aspect="aspect+GTiff" --json
32 
33  {
34  "module": "r.slope.aspect",
35  "id": "r.slope.aspect_1804289383",
36  "inputs":[
37  {"import_descr": {"source":"https://storage.googleapis.com/graas-geodata/elev_ned_30m.tif", "type":"raster"},
38  "param": "elevation", "value": "elevation"},
39  {"param": "format", "value": "degrees"},
40  {"param": "precision", "value": "FCELL"},
41  {"param": "zscale", "value": "1.0"},
42  {"param": "min_slope", "value": "0.0"}
43  ],
44  "outputs":[
45  {"export": {"format":"GTiff", "type":"raster"},
46  "param": "slope", "value": "slope"},
47  {"export": {"format":"GTiff", "type":"raster"},
48  "param": "aspect", "value": "aspect"}
49  ]
50  }
51 
52  v.out.ascii input="hospitals@PERMANENT" output="myfile+TXT" --json
53 
54  {
55  "module": "v.out.ascii",
56  "id": "v.out.ascii_1804289383",
57  "inputs":[
58  {"param": "input", "value": "hospitals@PERMANENT"},
59  {"param": "layer", "value": "1"},
60  {"param": "type", "value": "point,line,boundary,centroid,area,face,kernel"},
61  {"param": "format", "value": "point"},
62  {"param": "separator", "value": "pipe"},
63  {"param": "precision", "value": "8"}
64  ],
65  "outputs":[
66  {"export": {"format":"TXT", "type":"file"},
67  "param": "output", "value": "$file::myfile"}
68  ]
69  }
70 
71  v.info map="hospitals@PERMANENT" -c --json
72 
73  {
74  "module": "v.info",
75  "id": "v.info_1804289383",
76  "flags":"c",
77  "inputs":[
78  {"param": "map", "value": "hospitals@PERMANENT"},
79  {"param": "layer", "value": "1"}
80  ]
81  }
82 
83 
84  A process chain has the following form
85 
86 {
87  'list': [{
88  'module': 'g.region',
89  'id': 'g_region_1',
90  'inputs': [{'import_descr': {'source': 'https://storage.googleapis.com/graas-geodata/elev_ned_30m.tif',
91  'type': 'raster'},
92  'param': 'raster',
93  'value': 'elev_ned_30m_new'}],
94  'flags': 'p'
95  },
96  {
97  'module': 'r.slope.aspect',
98  'id': 'r_slope_aspect_1',
99  'inputs': [{'param': 'elevation',
100  'value': 'elev_ned_30m_new'}],
101  'outputs': [{'export': {'format': 'GTiff',
102  'type': 'raster'},
103  'param': 'slope',
104  'value': 'elev_ned_30m_new_slope'}],
105  'flags': 'a'},
106  {
107  'module': 'r.univar',
108  'id': 'r_univar_1',
109  'inputs': [{"import_descr": {"source": "LT52170762005240COA00",
110  "type": "landsat",
111  "landsat_atcor": "dos1"},
112  'param': 'map',
113  'value': 'LT52170762005240COA00_dos1.1'}],
114  'stdout': {'id': 'stats', 'format': 'kv', 'delimiter': '='},
115  'flags': 'a'
116  },
117  {
118  'module': 'exporter',
119  'id': 'exporter_1',
120  'outputs': [{'export': {'format': 'GTiff',
121  'type': 'raster'},
122  'param': 'map',
123  'value': 'LT52170762005240COA00_dos1.1'}]
124  },
125  {
126  "id": "ascii_out",
127  "module": "r.out.ascii",
128  "inputs": [{"param": "input",
129  "value": "elevation@PERMANENT"},
130  {"param": "precision", "value": "0"}],
131  "stdout": {"id": "elev_1", "format": "table", "delimiter": " "},
132  "flags": "h"
133  },
134  {
135  "id": "ascii_export",
136  "module": "r.out.ascii",
137  "inputs": [{"param": "input",
138  "value": "elevation@PERMANENT"}],
139  "outputs": [
140  {"export": {"type": "file", "format": "TXT"},
141  "param": "output",
142  "value": "$file::out1"}
143  ]
144  },
145  {
146  "id": "raster_list",
147  "module": "g.list",
148  "inputs": [{"param": "type",
149  "value": "raster"}],
150  "stdout": {"id": "raster", "format": "list", "delimiter": "\n"}
151  },
152  {
153  "module": "r.what",
154  "id": "r_what_1",
155  "verbose": True,
156  "flags": "nfic",
157  "inputs": [
158  {
159  "param": "map",
160  "value": "landuse96_28m@PERMANENT"
161  },
162  {
163  "param": "coordinates",
164  "value": "633614.08,224125.12,632972.36,225382.87"
165  },
166  {
167  "param": "null_value",
168  "value": "null"
169  },
170  {
171  "param": "separator",
172  "value": "pipe"
173  }
174  ],
175  "stdout": {"id": "sample", "format": "table", "delimiter": "|"}
176  }
177  ],
178  'webhooks': {'update': 'http://business-logic.company.com/api/v1/actinia-update-webhook',
179  'finished': 'http://business-logic.company.com/api/v1/actinia-finished-webhook'},
180  'version': '1'
181 }
182 
183 */
184 char *G__json(void)
185 {
186  FILE *fp = stdout;
187  /*FILE *fp = NULL;*/
188  char *type;
189  char *file_name = NULL;
190  int c;
191  int random_int = rand();
192  int num_flags = 0;
193  int num_inputs = 0;
194  int num_outputs = 0;
195  int i = 0;
196 
197  char age[KEYLENGTH];
198  char element[KEYLENGTH]; /*cell, file, grid3, vector */
199  char desc[KEYLENGTH];
200 
201  file_name = G_tempfile();
202 
203  /* fprintf(stderr, "Filename: %s\n", file_name); */
204  fp = fopen(file_name, "w+");
205  if (fp == NULL)
206  {
207  fprintf(stderr, "Unable to open temporary file <%s>\n", file_name);
208  exit(EXIT_FAILURE);
209  }
210 
211  if (st->n_flags) {
212  struct Flag *flag;
213  for (flag = &st->first_flag; flag; flag = flag->next_flag) {
214  if(flag->answer)
215  num_flags += 1;;
216  }
217  }
218 
219  /* Count input and output options */
220  if (st->n_opts) {
221  struct Option *opt;
222  for (opt = &st->first_option; opt; opt = opt->next_opt) {
223  if (opt->answer) {
224  if (opt->gisprompt) {
225  G__split_gisprompt(opt->gisprompt, age, element, desc);
226  /* fprintf(stderr, "age: %s element: %s desc: %s\n", age, element, desc); */
227  if (G_strncasecmp("new", age, 3) == 0) {
228  /*fprintf(fp, "new: %s\n", opt->gisprompt);*/
229  num_outputs += 1;
230  }
231  else {
232  /*fprintf(fp, "%s\n", opt->gisprompt);*/
233  num_inputs += 1;
234  }
235  } else {
236  num_inputs += 1;
237  }
238  }
239  }
240  }
241 
242  fprintf(fp, "{\n");
243  fprintf(fp, " \"module\": \"%s\",\n", G_program_name());
244  fprintf(fp, " \"id\": \"%s_%i\"", G_program_name(), random_int);
245 
246  if (st->n_flags && num_flags > 0) {
247  struct Flag *flag;
248  fprintf(fp, ",\n");
249  fprintf(fp, " \"flags\":\"");
250 
251  for (flag = &st->first_flag; flag; flag = flag->next_flag) {
252  if(flag->answer)
253  fprintf(fp, "%c", flag->key);
254  }
255  fprintf(fp, "\"");
256  }
257 
258  /* Print the input options
259  */
260  if (st->n_opts && num_inputs > 0) {
261  struct Option *opt;
262  i = 0;
263  fprintf(fp, ",\n");
264  fprintf(fp, " \"inputs\":[\n");
265  for (opt = &st->first_option; opt; opt = opt->next_opt) {
266  if (opt->gisprompt) {
267  G__split_gisprompt(opt->gisprompt, age, element, desc);
268  if (G_strncasecmp("new", age, 3) != 0) {
269  if (opt->answer) {
271  i++;
272  if (i < num_inputs) {fprintf(fp, ",\n");}
273  else {fprintf(fp, "\n");}
274  }
275  }
276  } else if (opt->answer) {
277  /* Check for input options */
278  fprintf(fp, " {\"param\": \"%s\", ", opt->key);
279  fprintf(fp, "\"value\": \"%s\"}", opt->answer);
280  i++;
281  if (i < num_inputs) {fprintf(fp, ",\n");}
282  else {fprintf(fp, "\n");}
283  }
284  }
285  fprintf(fp, " ]");
286  }
287 
288  /* Print the output options
289  */
290  if (st->n_opts && num_outputs > 0) {
291  struct Option *opt;
292  i = 0;
293  fprintf(fp, ",\n");
294  fprintf(fp, " \"outputs\":[\n");
295  for (opt = &st->first_option; opt; opt = opt->next_opt) {
296  if (opt->gisprompt) {
297  G__split_gisprompt(opt->gisprompt, age, element, desc);
298  if (G_strncasecmp("new", age, 3) == 0) {
299  if (opt->answer) {
301  i++;
302  if (i < num_outputs) {fprintf(fp, ",\n");}
303  else {fprintf(fp, "\n");}
304  }
305  }
306  }
307  }
308  fprintf(fp, " ]\n");
309  }
310 
311  fprintf(fp, "}\n");
312  fclose(fp);
313 
314  /* Print the file content to stdout */
315  fp = fopen(file_name, "r");
316  if (fp == NULL)
317  {
318  fprintf(stderr, "Unable to open temporary file <%s>\n", file_name);
319  exit(EXIT_FAILURE);
320  }
321 
322  c = fgetc(fp);
323  while (c != EOF)
324  {
325  fprintf (stdout, "%c", c);
326  c = fgetc(fp);
327  }
328  fclose(fp);
329 
330  return file_name;
331 }
332 
333 /* \brief Check the provided answer and generate the import statement
334  dependent on the element type (cell, vector, grid3, file)
335 
336  {'import_descr': {'source': 'https://storage.googleapis.com/graas-geodata/elev_ned_30m.tif',
337  'type': 'raster'},
338  'param': 'map',
339  'value': 'elevation'}
340  */
341 void check_create_import_opts(struct Option *opt, char *element, FILE *fp)
342 {
343  int i = 0;
344  int has_import = 0;
345  char **tokens;
346 
347  tokens = G_tokenize(opt->answer, "@");
348  while (tokens[i]) {
349  G_chop(tokens[i]);
350  i++;
351  }
352 
353  fprintf(fp, " {");
354 
355  if (i > 1) {
356  if (G_strncasecmp("cell", element, 4) == 0) {
357  fprintf(fp, "\"import_descr\": {\"source\":\"%s\", \"type\":\"raster\"},\n ", tokens[1]);
358  has_import = 1;
359  }
360  else if (G_strncasecmp("file", element, 4) == 0) {
361  fprintf(fp, "\"import_descr\": {\"source\":\"%s\", \"type\":\"file\"},\n ", tokens[1]);
362  has_import = 1;
363  }
364  else if (G_strncasecmp("vector", element, 4) == 0) {
365  fprintf(fp, "\"import_descr\": {\"source\":\"%s\", \"type\":\"vector\"},\n ", tokens[1]);
366  has_import = 1;
367  }
368  }
369 
370  fprintf(fp, "\"param\": \"%s\", ", opt->key);
371  /* In case of import the mapset must be removed always */
372  fprintf(fp, "\"value\": \"%s\"", check_mapset_in_layer_name(tokens[0], has_import));
373  fprintf(fp, "}");
374 
375  G_free_tokens(tokens);
376 }
377 
378 /* \brief Check the provided answer and generate the export statement
379  dependent on the element type (cell, vector, grid3, file)
380 
381  "outputs": [
382  {"export": {"type": "file", "format": "TXT"},
383  "param": "output",
384  "value": "$file::out1"},
385  {'export': {'format': 'GTiff', 'type': 'raster'},
386  'param': 'map',
387  'value': 'LT52170762005240COA00_dos1.1'}
388  ]
389  */
390 void check_create_export_opts(struct Option *opt, char *element, FILE *fp)
391 {
392  int i = 0;
393  int has_file_export = 0;
394  char **tokens;
395 
396  tokens = G_tokenize(opt->answer, "+");
397  while (tokens[i]) {
398  G_chop(tokens[i]);
399  i++;
400  }
401 
402  fprintf(fp, " {");
403 
404  if (i > 1) {
405  if (G_strncasecmp("cell", element, 4) == 0) {
406  fprintf(fp, "\"export\": {\"format\":\"%s\", \"type\":\"raster\"},\n ", tokens[1]);
407  }
408  else if (G_strncasecmp("file", element, 4) == 0) {
409  fprintf(fp, "\"export\": {\"format\":\"%s\", \"type\":\"file\"},\n ", tokens[1]);
410  has_file_export = 1;
411  }
412  else if (G_strncasecmp("vector", element, 4) == 0) {
413  fprintf(fp, "\"export\": {\"format\":\"%s\", \"type\":\"vector\"},\n ", tokens[1]);
414  }
415  }
416 
417  fprintf(fp, "\"param\": \"%s\", ", opt->key);
418  if (has_file_export == 1) {
419  fprintf(fp, "\"value\": \"$file::%s\"", check_mapset_in_layer_name(tokens[0], 1));
420  } else {
421  fprintf(fp, "\"value\": \"%s\"", check_mapset_in_layer_name(tokens[0], 1));
422  }
423  fprintf(fp, "}");
424 
425  G_free_tokens(tokens);
426 }
427 
428 /*
429  \brief Check if the current mapset is present in the layer name and remove it
430 
431  The flag always_remove tells this function to always remove all mapset names.
432 
433  \return pointer to the layer name without the current mapset
434 */
435 char *check_mapset_in_layer_name(char *layer_name, int always_remove)
436 {
437  int i = 0;
438  char **tokens;
439  const char *mapset;
440 
441  mapset = G_mapset();
442 
443  tokens = G_tokenize(layer_name, "@");
444 
445  while (tokens[i]) {
446  G_chop(tokens[i]);
447  /* fprintf(stderr, "Token %i: %s\n", i, tokens[i]); */
448  i++;
449  }
450 
451  if (always_remove == 1)
452  return tokens[0];
453 
454  if (i > 1 && G_strcasecmp(mapset, tokens[1]) == 0)
455  return tokens[0];
456 
457  return layer_name;
458 }
G_program_name
const char * G_program_name(void)
Return module name.
Definition: progrm_nme.c:28
G_strcasecmp
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition: strings.c:46
G_strncasecmp
int G_strncasecmp(const char *x, const char *y, int n)
String compare ignoring case (upper or lower) - limited number of characters.
Definition: strings.c:68
G_chop
char * G_chop(char *line)
Chop leading and trailing white spaces.
Definition: strings.c:286
G_mapset
const char * G_mapset(void)
Get current mapset name.
Definition: mapset.c:33
G__json
char * G__json(void)
This function generates actinia JSON process chain building blocks from the command line arguments th...
Definition: parser_json.c:184
G__split_gisprompt
void G__split_gisprompt(const char *gisprompt, char *age, char *element, char *desc)
Definition: parser.c:1633
check_mapset_in_layer_name
char * check_mapset_in_layer_name(char *, int)
Definition: parser_json.c:435
NULL
#define NULL
Definition: ccmath.h:32
check_create_import_opts
void check_create_import_opts(struct Option *, char *, FILE *)
Definition: parser_json.c:341
check_create_export_opts
void check_create_export_opts(struct Option *, char *, FILE *)
Definition: parser_json.c:390
G_free_tokens
void G_free_tokens(char **tokens)
Free memory allocated to tokens.
Definition: token.c:204
G_tempfile
char * G_tempfile(void)
Returns a temporary file name.
Definition: tempfile.c:62
element
Definition: lidar.h:88
G_tokenize
char ** G_tokenize(const char *buf, const char *delim)
Tokenize string.
Definition: token.c:48
st
struct state * st
Definition: parser.c:102