1 /*
2  * (C) Copyright 2005- ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  *
7  * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
8  * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
9  */
10 
11 /*
12  * C Implementation: grib_ls
13  *
14  */
15 #include "grib_tools.h"
16 
17 grib_option grib_options[] = {
18     /*  {id, args, help}, on, command_line, value*/
19     { "f", 0, 0, 1, 0, 0 },
20     { "p:", 0, 0, 0, 1, 0 },
21     { "F:", 0, 0, 1, 1, "%g" },
22     { "P:", 0, 0, 0, 1, 0 },
23     { "w:", 0, 0, 0, 1, 0 },
24     { "j", 0, "JSON output\n", 0, 1, 0 },
25     { "B:", 0, 0, 0, 1, 0 },
26     { "l:", 0, 0, 0, 1, 0 },
27     { "s:", 0, 0, 0, 1, 0 },
28     { "i:", 0, 0, 0, 1, 0 },
29     { "n:", 0, 0, 1, 1, "ls" },
30     { "m", 0, 0, 0, 1, 0 },
31     { "V", 0, 0, 0, 1, 0 },
32     { "W:", 0, 0, 1, 1, "10" },
33     { "S", 0, 0, 1, 0, 0 },
34     { "M", 0, 0, 0, 1, 0 },
35     { "H", 0, 0, 1, 0, 0 },
36     { "g", 0, 0, 0, 1, 0 },
37     { "P", 0, 0, 1, 0, 0 },
38     { "T:", 0, 0, 0, 1, 0 },
39     { "7", 0, 0, 0, 1, 0 },
40     { "v", 0, 0, 1, 0, 0 },
41     { "X:", 0, 0, 0, 1, 0 },
42     { "x", 0, 0, 0, 1, 0 }
43 };
44 
45 const char* tool_description =
46     "List content of GRIB files printing values of "
47     "some keys.\n\tIt does not fail when a key is not found.";
48 const char* tool_name   = "grib_ls";
49 const char* tool_usage  = "[options] grib_file grib_file ...";
50 static char* new_handle = "";
51 
52 int grib_options_count = sizeof(grib_options) / sizeof(grib_option);
53 static double lat      = 0;
54 static double lon      = 0;
55 static int mode        = 0;
56 /* Note:
57  * There are two JSON-output modes:
58  *  1. With a provided lat-lon for the nearest neighbour (options->latlon==1)
59  *  2. All other cases (options->json_output==1)
60  * The first is special and has a very different format. They need to be
61  * treated differently
62  */
63 static int json_latlon       = 0;
64 static int first_handle      = 1;
65 static grib_nearest* nearest = NULL;
66 
main(int argc,char * argv[])67 int main(int argc, char* argv[])
68 {
69     return grib_tool(argc, argv);
70 }
71 
72 /*
73  This is executed before processing the options with
74  getopt and therefore it is the right place for hacking
75  the arguments if needed
76 */
grib_tool_before_getopt(grib_runtime_options * options)77 int grib_tool_before_getopt(grib_runtime_options* options)
78 {
79     return 0;
80 }
81 
82 /*
83  The options have been parsed and the structure
84  grib_runtime_options* options has been loaded.
85  Initialisation and startup can be done here
86 */
grib_tool_init(grib_runtime_options * options)87 int grib_tool_init(grib_runtime_options* options)
88 {
89     char *theEnd = NULL, *end1 = NULL;
90     size_t size = 4;
91     int ret     = 0;
92     double min = 0, max = 0;
93     int i   = 0;
94     char* p = NULL;
95     if (options->latlon && grib_options_on("j")) {
96         options->verbose = 0;
97         json_latlon      = 1;
98     }
99 
100     if (options->latlon) {
101         lat = strtod(options->latlon, &theEnd);
102         if (*theEnd != ',') {
103             fprintf(stderr, "Error %s: wrong latitude value. Please use 'latitude,longitude'\n", tool_name);
104             exit(1);
105         }
106         lon = strtod(++theEnd, &end1);
107 
108         mode = GRIB_NEAREST_SAME_POINT | GRIB_NEAREST_SAME_GRID;
109 
110         if (end1 && *end1 == ',') {
111             end1++;
112             if (*end1 != '0') {
113                 p = end1;
114                 while (*p != ',' && *p != '\0')
115                     p++;
116                 if (*end1 == '4') {
117                     options->latlon_mode = 4;
118                 }
119                 else if (*end1 == '1') {
120                     options->latlon_mode = 1;
121                 }
122                 else {
123                     fprintf(stderr, "Error %s: wrong mode given in option -l\n", tool_name);
124                     exit(1);
125                 }
126             }
127             Assert(p);
128             if (p && *p == ',') {
129                 p++;
130                 options->latlon_mask = strdup(p);
131             }
132         }
133     }
134 
135     if (options->latlon && options->latlon_mask) {
136         grib_handle* hh;
137         int idx = 0, land_found = 0;
138         double min_overall = 0.0;
139         int idx_overall    = -1;
140         FILE* f            = fopen(options->latlon_mask, "r");
141         if (!f) {
142             perror(options->latlon_mask);
143             exit(1);
144         }
145         hh = grib_handle_new_from_file(0, f, &ret);
146         fclose(f);
147         GRIB_CHECK_NOLINE(ret, 0);
148         nearest = grib_nearest_new(hh, &ret);
149         GRIB_CHECK_NOLINE(ret, 0);
150         GRIB_CHECK_NOLINE(grib_nearest_find(nearest, hh, lat, lon, mode,
151                                             options->lats, options->lons, options->mask_values, options->distances, options->indexes, &size),
152                           0);
153         grib_nearest_delete(nearest);
154         nearest = NULL;
155         grib_handle_delete(hh);
156 
157         options->latlon_idx = -1;
158         max                 = options->distances[0];
159         for (i = 0; i < LATLON_SIZE; i++)
160             if (max < options->distances[i]) {
161                 max = options->distances[i];
162             }
163         min         = max;
164         min_overall = max;
165         /* See GRIB-213 */
166         for (i = 0; i < LATLON_SIZE; i++) {
167             if (min_overall >= options->distances[i]) { /* find overall min and index ignoring mask */
168                 min_overall = options->distances[i];
169                 idx_overall = i;
170             }
171             if ((min >= options->distances[i]) && (options->mask_values[i] >= 0.5)) {
172                 land_found = 1; /* found at least one point which is land */
173                 min        = options->distances[i];
174                 idx        = i;
175             }
176         }
177         if (land_found) {
178             options->latlon_idx = idx;
179         }
180         else {
181             options->latlon_idx = idx_overall; /* all points were sea, so pick the min overall */
182         }
183 
184         if (options->latlon_idx < 0) {
185             min                 = 0;
186             options->latlon_idx = 0;
187             for (i = 1; i < LATLON_SIZE; i++)
188                 if (min > options->distances[i]) {
189                     min                 = options->distances[i];
190                     options->latlon_idx = i;
191                 }
192         }
193     }
194     if (json_latlon)
195         printf("[\n");
196 
197     return 0;
198 }
199 
200 /*
201  A new file is being parsed. The file name is file. This function is called every time
202  a new input file name is processed, before opening the file.
203 */
grib_tool_new_filename_action(grib_runtime_options * options,const char * file)204 int grib_tool_new_filename_action(grib_runtime_options* options, const char* file)
205 {
206     return 0;
207 }
208 
grib_tool_new_file_action(grib_runtime_options * options,grib_tools_file * file)209 int grib_tool_new_file_action(grib_runtime_options* options, grib_tools_file* file)
210 {
211     exit_if_input_is_directory(tool_name, file->name);
212     if (nearest)
213         grib_nearest_delete(nearest);
214     nearest = NULL;
215     return 0;
216 }
217 
print_key_values(grib_runtime_options * options,grib_handle * h)218 static void print_key_values(grib_runtime_options* options, grib_handle* h)
219 {
220     int i;
221     int ret       = 0;
222     char* s       = "\"keys\" : {";
223     double dvalue = 0;
224     long lvalue   = 0;
225     char value[MAX_STRING_LEN];
226     size_t len = MAX_STRING_LEN;
227     for (i = 0; i < options->print_keys_count; i++) {
228         ret = GRIB_SUCCESS;
229         printf("%s", s);
230         len = MAX_STRING_LEN;
231         printf("\"%s\" : ", options->print_keys[i].name);
232         if (grib_is_missing(h, options->print_keys[i].name, &ret) && ret == GRIB_SUCCESS)
233             printf("\"missing\"");
234         else if (ret == GRIB_SUCCESS) {
235             if (options->print_keys[i].type == GRIB_TYPE_UNDEFINED)
236                 grib_get_native_type(h, options->print_keys[i].name, &(options->print_keys[i].type));
237             switch (options->print_keys[i].type) {
238                 case GRIB_TYPE_STRING:
239                     ret = grib_get_string(h, options->print_keys[i].name, value, &len);
240                     printf("\"%s\"", value);
241                     break;
242                 case GRIB_TYPE_DOUBLE:
243                     ret = grib_get_double(h, options->print_keys[i].name, &dvalue);
244                     printf("%g", dvalue);
245                     break;
246                 case GRIB_TYPE_LONG:
247                     ret = grib_get_long(h, options->print_keys[i].name, &lvalue);
248                     printf("%ld", lvalue);
249                     break;
250                 default:
251                     printf("invalid_type");
252                     break;
253             }
254         }
255         if (ret == GRIB_NOT_FOUND)
256             printf("null");
257         s = ", ";
258     }
259     printf("}");
260 }
261 
262 /*
263  A new handle is available from the current input file and can be processed here.
264  The handle available in this function is in the set of messages satisfying the constraint
265  of the -w option. They are not to be skipped.
266 */
grib_tool_new_handle_action(grib_runtime_options * options,grib_handle * h)267 int grib_tool_new_handle_action(grib_runtime_options* options, grib_handle* h)
268 {
269     size_t size = 4;
270     double v    = 0;
271     int err     = 0;
272     int i;
273 
274     if (!options->skip) {
275         if (options->set_values_count != 0)
276             err = grib_set_values(h, options->set_values, options->set_values_count);
277 
278         if (err != GRIB_SUCCESS && options->fail)
279             exit(err);
280     }
281 
282     if (options->latlon) {
283         double min;
284         err = 0;
285         if (!nearest)
286             nearest = grib_nearest_new(h, &err);
287         if (err == GRIB_NOT_IMPLEMENTED) {
288             char grid_type[100];
289             size_t grid_type_len = 100;
290             int err1             = grib_get_string(h, "gridType", grid_type, &grid_type_len);
291             if (err1 == GRIB_SUCCESS) {
292                 fprintf(stderr, "Nearest neighbour functionality is not supported for grid type: %s\n", grid_type);
293             }
294         }
295         GRIB_CHECK_NOLINE(err, 0);
296         {
297             int nn_flag = 0;
298             if (options->latlon_mask) {
299                 nn_flag = mode; /* ECC-638 */
300             }
301             GRIB_CHECK_NOLINE(grib_nearest_find(nearest, h, lat, lon, nn_flag,
302                                                 options->lats, options->lons, options->values,
303                                                 options->distances, options->indexes, &size),
304                               0);
305         }
306 
307         if (!options->latlon_mask) {
308             min                 = options->distances[0];
309             options->latlon_idx = 0;
310             for (i = 1; i < LATLON_SIZE; i++) {
311                 if (min > options->distances[i]) {
312                     min                 = options->distances[i];
313                     options->latlon_idx = i;
314                 }
315             }
316         }
317 
318         if (json_latlon) {
319             char* s             = "\n[\n";
320             double missingValue = 9999;
321             char value[MAX_STRING_LEN];
322             size_t len = MAX_STRING_LEN;
323             printf("%s", new_handle);
324             printf("{\n");
325             print_key_values(options, h);
326             printf("\n, \"selected\" : %d", options->latlon_idx);
327             printf(", \"method\" : ");
328             if (options->latlon_mask)
329                 printf("\"nearest_land\"");
330             else
331                 printf("\"nearest\"");
332             printf("\n, \"neighbours\" : ");
333             for (i = 0; i < LATLON_SIZE; i++) {
334                 printf("%s", s);
335                 len = MAX_STRING_LEN;
336                 printf(
337                     "{\"index\" : %d, \"latitude\" : %g, \"longitude\" : %g, \"distance\" : %g, "
338                     "\"distance_unit\" : \"km\", ",
339                     (int)options->indexes[i], options->lats[i], options->lons[i],
340                     options->distances[i]);
341                 if (grib_get_double_element(h, "values", options->indexes[i], &v) == GRIB_SUCCESS) {
342                     if (v == missingValue)
343                         printf("\"value\" : null ");
344                     else
345                         printf("\"value\" : %g ", v);
346                 }
347 
348                 if (grib_get_string(h, "units", value, &len) == GRIB_SUCCESS)
349                     printf(", \"unit\" : \"%s\"", value);
350 
351                 if (options->latlon_mask)
352                     printf(", \"mask_value\" : %.2f", options->mask_values[i]);
353                 printf("}");
354                 s = "\n,";
355             }
356 
357             printf("\n]");
358             printf("\n}");
359         }
360     }
361 
362     if (!json_latlon && options->json_output) {
363         if (options->current_infile && options->current_infile->name) {
364             size_t len = strlen(options->current_infile->name);
365             grib_set_string(h, "file", options->current_infile->name, &len);
366         }
367         if (!first_handle && options->handle_count > 1) {
368             fprintf(stdout, ",\n");
369         }
370         if (options->json_output && first_handle) {
371             fprintf(stdout, "{ \"messages\" : [ \n");
372             first_handle = 0;
373         }
374     }
375     new_handle = "\n,";
376     return 0;
377 }
378 
379 /*
380  A new handle to skip is available. At this point something can be done
381  with the message to be skipped before deleting the handle
382 */
grib_tool_skip_handle(grib_runtime_options * options,grib_handle * h)383 int grib_tool_skip_handle(grib_runtime_options* options, grib_handle* h)
384 {
385     grib_handle_delete(h);
386     return 0;
387 }
388 
389 /* key values can be printed here. Headers are already printed if requested */
grib_tool_print_key_values(grib_runtime_options * options,grib_handle * h)390 void grib_tool_print_key_values(grib_runtime_options* options, grib_handle* h)
391 {
392     grib_print_key_values(options, h);
393 }
394 
395 /* This is executed after the last message in the last file is processed */
grib_tool_finalise_action(grib_runtime_options * options)396 int grib_tool_finalise_action(grib_runtime_options* options)
397 {
398     int i = 0;
399     if (options->latlon && options->verbose) {
400         printf("Input Point: latitude=%.2f  longitude=%.2f\n", lat, lon);
401         if (options->latlon_idx >= 0 && options->latlon_idx < LATLON_SIZE) {
402             printf("Grid Point chosen #%d index=%d latitude=%.2f longitude=%.2f distance=%.2f (Km)\n",
403                    options->latlon_idx + 1, (int)options->indexes[options->latlon_idx],
404                    options->lats[options->latlon_idx],
405                    options->lons[options->latlon_idx],
406                    options->distances[options->latlon_idx]);
407         }
408         if (options->latlon_mask) {
409             printf("Mask values:\n");
410             for (i = 0; i < LATLON_SIZE; i++) {
411                 printf("- %d - index=%d latitude=%.2f longitude=%.2f distance=%.2f (Km) value=%.2f\n",
412                        i + 1, (int)options->indexes[i], options->lats[i], options->lons[i],
413                        options->distances[i], options->mask_values[i]);
414             }
415         }
416         else {
417             printf("Other grid Points\n");
418             for (i = 0; i < LATLON_SIZE; i++) {
419                 printf("- %d - index=%d latitude=%.2f longitude=%.2f distance=%.2f (Km)\n",
420                        i + 1, (int)options->indexes[i], options->lats[i], options->lons[i],
421                        options->distances[i]);
422             }
423         }
424     }
425 
426     if (!json_latlon && options->json_output)
427         fprintf(stdout, "\n]}\n");
428 
429     if (nearest)
430         grib_nearest_delete(nearest);
431     if (json_latlon)
432         printf("\n]\n");
433 
434     return 0;
435 }
436 
grib_no_handle_action(grib_runtime_options * options,int err)437 int grib_no_handle_action(grib_runtime_options* options, int err)
438 {
439     if (options->json_output) {
440         if (first_handle) {
441             fprintf(dump_file, "{ \"messages\" : [ \n");
442             first_handle = 0;
443         }
444         else {
445             fprintf(dump_file, ",\n");
446         }
447     }
448     fprintf(dump_file, "\t\t\"ERROR: unreadable message\"\n");
449     return 0;
450 }
451