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