parser_json.c 14 KB

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