OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
val_extract.c
Go to the documentation of this file.
1 // implemented version numbers
2 #define VALEXTRACT_IMPLEMENTATION 2006007
3 #define VALEXTRACT_IMPLEMENTATION_STR "2.6.7"
4 #define VALEXTRACT_API_VERSION 2006005
5 #define VALEXTRACT_API_VERSION_STR "2.6.5"
6 
7 #include "val_extract.h"
8 
9 #include <argpar.h>
10 #include <olog.h>
11 #include <olog/loader.h>
12 #include <pqueue.h>
13 #include <shash.h>
14 #include <vincenty.h>
15 
16 #include <ctype.h>
17 #include <errno.h>
18 #include <float.h>
19 #include <libgen.h>
20 #include <limits.h>
21 #include <math.h>
22 #include <netcdf.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 extern long timezone;
30 #include <unistd.h>
31 
32 #define str(s) #s
33 #define expanded_str(s) str(s)
34 
35 static double strtod_strict(const char* str); // should make a strict parser library
36 
37 static const char *FLAG_MEANINGS_SEP = " ";
38 
39 #define VALEXTRACT_OPTICS_DEFAULT 20
40 
41 static const char *skip_stats[] = { "l2_flags", "flag_sst4", "flags_sst" };
42 //static const char *skip_stats[] = { NULL };
43 
44 #define START_LINE_KEY 1
45 #define END_LINE_KEY 2
46 #define START_PIXEL_KEY 3
47 #define END_PIXEL_KEY 4
48 #define BOX_SIZE_KEY 5
49 
50 #define BOX_SIZE_KM_KEY 6
51 #define SLAT_KEY 7
52 #define ELAT_KEY 8
53 #define SLON_KEY 9
54 #define ELON_KEY 10
55 
56 #define L2QC_KEY 11
57 #define RANGES_KEY 12
58 #define IGNORE_KEY 13
59 #define OPTICS_KEY 14
60 #define COUNT_KEY 15
61 #define GLOBAL_ATT_KEY 16
62 #define VAR_ATT_KEY 17
63 #define SKIP_STATS_KEY 18
64 
65 #define ARG_INT_START START_LINE_KEY
66 #define ARG_INT_END BOX_SIZE_KEY
67 
68 #define ARG_DBL_START BOX_SIZE_KM_KEY
69 #define ARG_DBL_END ELON_KEY
70 
71 static argpar_option options[] = {
72  { "ifile", 'f', "FILE", 0, "input NetCDF file" },
73  { "optics_threshold", OPTICS_KEY, "N", OPTION_DBL | OPTION_HIDDEN, "OPTICS cluster threshold" },
74  { 0, 0, 0, 0, "For validation box extracts, using pixels/lines:" },
75  { "sline", START_LINE_KEY, 0, OPTION_INT, "target line, zero-based (positive integer)" },
76  { "spixl", START_PIXEL_KEY, 0, OPTION_INT, "target pixel, zero-based (positive integer)" },
77  { "boxsize", BOX_SIZE_KEY, "N", OPTION_INT, "size of box (N by N pixels) (positive integer)" },
78  { "boxsizekm", BOX_SIZE_KM_KEY, "N", OPTION_DBL, "grab all pixels within N kilometers (positive integer)" },
79  { 0, 0, 0, 0, "For region extracts, using pixels/lines:" },
80  { "sline", START_LINE_KEY, 0, OPTION_INT, "starting line, zero-based (positive integer)" },
81  { "eline", END_LINE_KEY, 0, OPTION_INT, "ending line, zero-based (positive integer)" },
82  { "spixl", START_PIXEL_KEY, 0, OPTION_INT, "starting pixel, zero-based (positive integer)" },
83  { "epixl", END_PIXEL_KEY, 0, OPTION_INT, "ending pixel, zero-based (positive integer)" },
84  { 0, 0, 0, 0, "For validation box extracts, using lat/lon:" },
85  { "slat", SLAT_KEY, 0, OPTION_DBL, "latitude target lies on" },
86  { "slon", SLON_KEY, 0, OPTION_DBL, "longitude target lies on" },
87  { "boxsize", BOX_SIZE_KEY, "N", OPTION_INT, "size of box (N by N pixels) (positive integer)" },
88  { "boxsizekm", BOX_SIZE_KM_KEY, "N", OPTION_DBL, "grab all pixels within N kilometers (positive integer)" },
89  { 0, 0, 0, 0, "For region extracts, using lat/lon:" },
90  { "slat", SLAT_KEY, 0, OPTION_DBL, "starting latitude (south-most boundary)" },
91  { "elat", ELAT_KEY, 0, OPTION_DBL, "ending latitude (north-most boundary)" },
92  { "slon", SLON_KEY, 0, OPTION_DBL, "starting longitude (west-most boundary)" },
93  { "elon", ELON_KEY, 0, OPTION_DBL, "ending longitude (east-most boundary)" },
94  { 0, 0, 0, OPTION_DOC, "If no location information is given, the entire file is processed." },
95  { 0, 0, 0, 0, "Extra information options:" },
96  { "global_att", GLOBAL_ATT_KEY, "1", OPTION_INT, "read all global attributes" },
97  { "variable_att", VAR_ATT_KEY, "1", OPTION_INT, "read all variable attributes" },
98  { 0, 0, 0, 0, "Filtering options:" },
99  { "l2qc", L2QC_KEY, "FLAG=decimal ...", 0, "Max percentages (in decimal form) an extract can contain "
100  "of a flag before failing the Level-2 Quality Checker (L2QC)." },
101  { "valid_ranges", RANGES_KEY, "var_prefix=min:max ...", 0, "Valid ranges for variables. Any values "
102  "outside these ranges will not be used for statistics." },
103  { "ignore_flags", IGNORE_KEY, "FLAG ...", 0, "Ignore values for ALL variables in pixels containing "
104  "the flag(s) listed." },
105  { "skip_stats", SKIP_STATS_KEY, "PRODUCT ...", 0, "Don't produce stats for given products." },
106  { "count_flags", COUNT_KEY, "FLAG ...", 0, "Count the number of pixels containing ANY of the flag(s) "
107  "listed." },
108  { 0, 0, 0, OPTION_DOC | OPTION_DOC_NO_BREAK, "Examples:" },
109  { 0, 0, 0, OPTION_DOC | OPTION_DOC_NO_BREAK, " l2qc=CLDICE=0.5 HIGLINT=0.25" },
110  { 0, 0, 0, OPTION_DOC | OPTION_DOC_NO_BREAK, " valid_ranges=Rrs_=0.001:0.02 chlor_a=0:10" },
111  { 0, 0, 0, OPTION_DOC, " ignore_flags=CLDICE HIGLINT" },
112  { 0, 0, 0, OPTION_DOC, "The flag names for the L2QC thresholds and to ignore them will be matched exactly "
113  "and this will error if that flag is not found. valid_ranges uses case-sensitive prefix matching. "
114  "All numbers are inclusive, e.g., 50% of pixels having the cloud flag passes QC in the "
115  "above example and 10 is a valid chlor_a value." },
116  { 0, 0, 0, OPTION_DOC, "Failing the L2QC will successfully process the file, but will not process any "
117  "variables within the file." },
118  { 0, 0, 0, 0, "Caveats:" },
119  { 0, 0, 0, OPTION_DOC, "If both boxsize and boxsizekm are set, boxsize is used." },
120  { 0, 0, 0, OPTION_DOC, "The \"center pixel\" for lat-/lon-based regions is only guaranteed to be inside the "
121  "actual data, but isn't necessarily the actual center due to weirdly shaped granules and endless edge "
122  "cases involving partial polygonal collision. Line- and pixel-based should be more accurate, but "
123  "YMMV. The detected center is guaranteed to be consistent and repeatable." },
124  { 0 } // tell argpar to stop checking options
125 };
126 
128  if (arguments) {
129  arguments->ifile = NULL;
130  arguments->box_size = VALEXTRACT_UNSET;
131  arguments->box_size_km = VALEXTRACT_UNSET;
132  arguments->optics_threshold = VALEXTRACT_OPTICS_DEFAULT;
133  arguments->start_line = VALEXTRACT_UNSET;
134  arguments->end_line = VALEXTRACT_UNSET;
135  arguments->start_pixel = VALEXTRACT_UNSET;
136  arguments->end_pixel = VALEXTRACT_UNSET;
137  arguments->start_lat = VALEXTRACT_UNSET;
138  arguments->end_lat = VALEXTRACT_UNSET;
139  arguments->start_lon = VALEXTRACT_UNSET;
140  arguments->end_lon = VALEXTRACT_UNSET;
141  arguments->product_count = 0;
142  arguments->count_flag_count = 0;
143  arguments->skip_stats = (char**)skip_stats;
144  arguments->skip_stats_count = sizeof(skip_stats)/sizeof(char*);
145  arguments->ignore_flag_count = 0;
146  arguments->l2qc_threshold_count = 0;
147  arguments->valid_range_count = 0;
148  arguments->global_atts = 0;
149  arguments->variable_atts = 0;
150  arguments->log = olog_load_default();
151  }
152 }
153 
154 static int _check_positioning(val_extract_arguments *arguments) {
155  if (arguments->box_size > 0 || arguments->box_size_km > 0) {
156  if (arguments->start_line == VALEXTRACT_UNSET && arguments->start_pixel == VALEXTRACT_UNSET && arguments->start_lat == VALEXTRACT_UNSET && arguments->start_lon == VALEXTRACT_UNSET) {
157  olog_crit(arguments->log, "sline and spixl or slat and slon are required if boxsize is provided\n");
158  return ARGPAR_ERR_USAGE;
159  } else if (arguments->start_lat != VALEXTRACT_UNSET && arguments->start_lon != VALEXTRACT_UNSET) {
160  arguments->lat_and_lon = true;
161  if (arguments->start_lat < -90 || arguments->start_lat > 90 || arguments->start_lon < -180 || arguments->start_lon > 180) {
162  olog_crit(arguments->log, "slat or slon is out of bounds\n");
163  return ARGPAR_ERR_USAGE;
164  }
165  } else if (arguments->start_line < 0 || arguments->start_pixel < 0) {
166  olog_crit(arguments->log, "sline and spixl are both required and must be positive integers\n");
167  return ARGPAR_ERR_USAGE;
168  } else {
169  arguments->line_and_pixel = true;
170  }
171  } else if (arguments->box_size < 0 && arguments->box_size != VALEXTRACT_UNSET) {
172  olog_crit(arguments->log, "boxsize must be a positive integer\n");
173  return ARGPAR_ERR_USAGE;
174  } else if (arguments->box_size_km < 0 && arguments->box_size_km != VALEXTRACT_UNSET) {
175  olog_crit(arguments->log, "boxsizekm must be a positive integer\n");
176  return ARGPAR_ERR_USAGE;
177  } else if (arguments->start_lat != VALEXTRACT_UNSET || arguments->end_lat != VALEXTRACT_UNSET || arguments->start_lon != VALEXTRACT_UNSET || arguments->end_lon != VALEXTRACT_UNSET) {
178  if (arguments->start_lat < -90 || arguments->start_lat > 90 || arguments->start_lon < -180 || arguments->start_lon > 180) {
179  olog_crit(arguments->log, "slat or slon is out of bounds or was unspecified\n");
180  return VALEXTRACT_ERR_INPUT;
181  } else if (arguments->end_lat < -90 || arguments->end_lat > 90 || arguments->end_lon < -180 || arguments->end_lon > 180) {
182  olog_crit(arguments->log, "elat or elon is out of bounds or was unspecified\n");
183  return VALEXTRACT_ERR_INPUT;
184  } else if (arguments->box_size == VALEXTRACT_UNSET) {
185  if (arguments->start_lat > arguments->end_lat) {
186  olog_crit(arguments->log, "slat must be less than elat\n");
187  return VALEXTRACT_ERR_INPUT;
188  } else if (arguments->start_lon > arguments->end_lon) {
189  olog_crit(arguments->log, "slon must be less than elon\n");
190  return VALEXTRACT_ERR_INPUT;
191  }
192  }
193  arguments->lat_and_lon = true;
194  } else if (arguments->start_line != VALEXTRACT_UNSET || arguments->end_line != VALEXTRACT_UNSET || arguments->start_pixel != VALEXTRACT_UNSET || arguments->end_pixel != VALEXTRACT_UNSET) {
195  if (arguments->start_line < 0 || arguments->end_line < 0 || arguments->start_pixel < 0 || arguments->end_pixel < 0) {
196  olog_crit(arguments->log, "sline, eline, spixl, and epixl are all required and must be positive integers\n");
197  return VALEXTRACT_ERR_INPUT;
198  } else if (arguments->start_line > arguments->end_line) {
199  olog_crit(arguments->log, "sline must be less than eline\n");
200  return VALEXTRACT_ERR_INPUT;
201  } else if (arguments->start_pixel > arguments->end_pixel) {
202  olog_crit(arguments->log, "spixl must be less than epixl\n");
203  return VALEXTRACT_ERR_INPUT;
204  }
205  arguments->line_and_pixel = true;
206  } else {
207  arguments->start_lat = -90;
208  arguments->end_lat = 90;
209  arguments->start_lon = -180;
210  arguments->end_lon = 180;
211  arguments->lat_and_lon = true;
212  }
213  return 0;
214 }
215 
216 static int parse_options(int key, char *argv, struct argpar_state *state) {
217  int ret = 0;
218 
220  if (key >= ARG_INT_START && key <= ARG_INT_END && state->argv_as_int_err) {
221  olog_crit(arguments->log, "Invalid cast for %s\n", state->arg_name);
222  ret = ARGPAR_ERR_ABORT;
223  } else if (key >= ARG_DBL_START && key <= ARG_DBL_END && state->argv_as_dbl_err) {
224  olog_crit(arguments->log, "Invalid cast for %s\n", state->arg_name);
225  ret = ARGPAR_ERR_ABORT;
226  } else {
227  switch (key) {
228  case 'h':
229  return ARGPAR_ERR_USAGE;
230  case 'f':
231  arguments->ifile = argv;
232  break;
233  case BOX_SIZE_KEY:
234  arguments->box_size = state->argv_as_int;
235  break;
236  case BOX_SIZE_KM_KEY:
237  arguments->box_size_km = state->argv_as_dbl;
238  break;
239  case START_LINE_KEY:
240  arguments->start_line = state->argv_as_int;
241  break;
242  case END_LINE_KEY:
243  arguments->end_line = state->argv_as_int;
244  break;
245  case START_PIXEL_KEY:
246  arguments->start_pixel = state->argv_as_int;
247  break;
248  case END_PIXEL_KEY:
249  arguments->end_pixel = state->argv_as_int;
250  break;
251  case SLAT_KEY:
252  arguments->start_lat = state->argv_as_dbl;
253  break;
254  case ELAT_KEY:
255  arguments->end_lat = state->argv_as_dbl;
256  break;
257  case SLON_KEY:
258  arguments->start_lon = state->argv_as_dbl;
259  break;
260  case ELON_KEY:
261  arguments->end_lon = state->argv_as_dbl;
262  break;
263  case GLOBAL_ATT_KEY:
264  arguments->global_atts = state->argv_as_int;
265  break;
266  case VAR_ATT_KEY:
267  arguments->variable_atts = state->argv_as_int;
268  break;
269  case L2QC_KEY:
270  if (strlen(argv)) {
271  char tmp[strlen(argv) + 1];
272  strncpy(tmp, argv, strlen(argv));
273  tmp[strlen(argv)] = '\0';
274  char *tmp_p = argv;
275  int space_count = 0;
276  if (!isspace(*tmp_p)) {
277  ++space_count;
278  }
279  while (*tmp_p) {
280  if (isspace(*tmp_p)) {
281  ++space_count;
282  }
283  ++tmp_p;
284  }
285 
286  arguments->l2qc_thresholds = calloc(space_count, sizeof(const char*));
287  if (!arguments->l2qc_thresholds) {
288  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
289  return ARGPAR_ERR_ABORT;
290  }
291  arguments->l2qc_flags = calloc(space_count, sizeof(const char*));
292  if (!arguments->l2qc_flags) {
293  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
294  return ARGPAR_ERR_ABORT;
295  }
296 
297  int i = 0;
298  char *token = strtok(tmp, " ");
299  while (token) {
300  if (strlen(token)) {
301  char *tmp_v = token;
302  char *tmp_k = strsep(&tmp_v, "=");
303  if (!tmp_v || !tmp_k) {
304  olog_crit(arguments->log, "Invalid L2QC spec: %s.\n", token);
305  return ARGPAR_ERR_ABORT;
306  }
307  arguments->l2qc_flags[i] = malloc((strlen(tmp_k) + 1) * sizeof(char));
308  if (!arguments->l2qc_flags[i]) {
309  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
310  return ARGPAR_ERR_ABORT;
311  }
312  strcpy(arguments->l2qc_flags[i], tmp_k);
313  arguments->l2qc_threshold_count++;
314  arguments->l2qc_thresholds[i] = strtod_strict(tmp_v);
315  if (errno) {
316  olog_crit(arguments->log, "Invalid L2QC spec: %s=%s.\n", tmp_k, tmp_v);
317  return ARGPAR_ERR_ABORT;
318  }
319  if (arguments->l2qc_thresholds[i] < 0) {
320  olog_crit(arguments->log, "L2QC thresholds cannot be negative: %s=%s.\n", tmp_k, tmp_v);
321  return ARGPAR_ERR_ABORT;
322  }
323  ++i;
324  }
325  token = strtok(NULL, " ");
326  }
327  }
328  break;
329  case RANGES_KEY:
330  if (strlen(argv)) {
331  char tmp[strlen(argv) + 1];
332  strncpy(tmp, argv, strlen(argv));
333  tmp[strlen(argv)] = '\0';
334  char *tmp_p = argv;
335  int space_count = 0;
336  if (!isspace(*tmp_p)) {
337  ++space_count;
338  }
339  while (*tmp_p) {
340  if (isspace(*tmp_p)) {
341  ++space_count;
342  }
343  ++tmp_p;
344  }
345 
346  arguments->valid_ranges = calloc(space_count, sizeof(val_extract_valid_range));
347  if (!arguments->valid_ranges) {
348  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
349  return ARGPAR_ERR_ABORT;
350  }
351 
352  int i = 0;
353  char *token = strtok(tmp, " ");
354  while (token) {
355  if (strlen(token)) {
356  char *tmp_v = token;
357  char *tmp_k = strsep(&tmp_v, "=");
358  if (!tmp_v || !tmp_k) {
359  olog_crit(arguments->log, "Invalid range spec: %s.\n", token);
360  return ARGPAR_ERR_ABORT;
361  }
362  arguments->valid_ranges[i].prefix = malloc((strlen(tmp_k) + 1) * sizeof(char));
363  if (!arguments->valid_ranges[i].prefix) {
364  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
365  return ARGPAR_ERR_ABORT;
366  }
367  strcpy(arguments->valid_ranges[i].prefix, tmp_k);
368  arguments->valid_range_count++;
369 
370  char *tmp_max = tmp_v;
371  char *tmp_min = strsep(&tmp_max, ":");
372 
373  arguments->valid_ranges[i].min = strtod_strict(tmp_min);
374  int error = errno;
375  arguments->valid_ranges[i].max = strtod_strict(tmp_max);
376  error |= errno;
377  if (error) {
378  olog_crit(arguments->log, "Invalid range spec: %s=%s:%s.\n", tmp_k, tmp_min, tmp_max);
379  return ARGPAR_ERR_ABORT;
380  }
381  ++i;
382  }
383  token = strtok(NULL, " ");
384  }
385  }
386  break;
387  case IGNORE_KEY:
388  if (strlen(argv)) {
389  char tmp[strlen(argv) + 1];
390  strncpy(tmp, argv, strlen(argv));
391  tmp[strlen(argv)] = '\0';
392  char *tmp_p = argv;
393  int space_count = 0;
394  if (!isspace(*tmp_p)) {
395  ++space_count;
396  }
397  while (*tmp_p) {
398  if (isspace(*tmp_p)) {
399  ++space_count;
400  }
401  ++tmp_p;
402  }
403 
404  arguments->ignore_flags = calloc(space_count, sizeof(const char*));
405  if (!arguments->ignore_flags) {
406  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
407  return ARGPAR_ERR_ABORT;
408  }
409 
410  int i = 0;
411  char *token = strtok(tmp, " ");
412  while (token) {
413  if (strlen(token)) {
414  arguments->ignore_flags[i] = malloc((strlen(token) + 1) * sizeof(char));
415  if (!arguments->ignore_flags[i]) {
416  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
417  return ARGPAR_ERR_ABORT;
418  }
419  strcpy(arguments->ignore_flags[i], token);
420  arguments->ignore_flag_count++;
421  ++i;
422  }
423  token = strtok(NULL, " ");
424  }
425  }
426  break;
427  case SKIP_STATS_KEY:
428  if (strlen(argv)) {
429  char tmp[strlen(argv) + 1];
430  strncpy(tmp, argv, strlen(argv));
431  tmp[strlen(argv)] = '\0';
432  char *tmp_p = argv;
433  int space_count = 0;
434  if (!isspace(*tmp_p)) {
435  ++space_count;
436  }
437  while (*tmp_p) {
438  if (isspace(*tmp_p)) {
439  ++space_count;
440  }
441  ++tmp_p;
442  }
443 
444  arguments->skip_stats = calloc(space_count, sizeof(const char*));
445  if (!arguments->skip_stats) {
446  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
447  return ARGPAR_ERR_ABORT;
448  }
449 
450  int i = 0;
451  char *token = strtok(tmp, " ");
452  while (token) {
453  if (strlen(token)) {
454  arguments->skip_stats[i] = malloc((strlen(token) + 1) * sizeof(char));
455  if (!arguments->skip_stats[i]) {
456  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
457  return ARGPAR_ERR_ABORT;
458  }
459  strcpy(arguments->skip_stats[i], token);
460  arguments->skip_stats_count++;
461  ++i;
462  }
463  token = strtok(NULL, " ");
464  }
465  }
466  break;
467  case COUNT_KEY:
468  if (strlen(argv)) {
469  char tmp[strlen(argv) + 1];
470  strncpy(tmp, argv, strlen(argv));
471  tmp[strlen(argv)] = '\0';
472  char *tmp_p = argv;
473  int space_count = 0;
474  if (!isspace(*tmp_p)) {
475  ++space_count;
476  }
477  while (*tmp_p) {
478  if (isspace(*tmp_p)) {
479  ++space_count;
480  }
481  ++tmp_p;
482  }
483 
484  arguments->count_flags = calloc(space_count, sizeof(const char*));
485  if (!arguments->count_flags) {
486  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
487  return ARGPAR_ERR_ABORT;
488  }
489 
490  int i = 0;
491  char *token = strtok(tmp, " ");
492  while (token) {
493  if (strlen(token)) {
494  arguments->count_flags[i] = malloc((strlen(token) + 1) * sizeof(char));
495  if (!arguments->count_flags[i]) {
496  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
497  return ARGPAR_ERR_ABORT;
498  }
499  strcpy(arguments->count_flags[i], token);
500  arguments->count_flag_count++;
501  ++i;
502  }
503  token = strtok(NULL, " ");
504  }
505  }
506  break;
507  case ARGPAR_KEY_INIT:
509  break;
510  case ARGPAR_KEY_SUCCESS:
511  if (arguments->ifile == NULL) {
512  olog_crit(arguments->log, "input file required\n");
513  ret = ARGPAR_ERR_USAGE;
514  } else {
515  ret = _check_positioning(arguments);
516  }
517  break;
518  }
519  }
520  return ret;
521 }
522 
523 argpar val_extract_argpar = { options, parse_options };
524 
525 static void print_time();
526 static bool starts_with(const char *str, const char *prefix);
527 static void calc_dim_multipliers(int ndims, const int *dims, int *dim_multipliers);
528 static int flatten_index(int ndims, const int *dim_multipliers, int *index);
529 //static void unflatten_index(int index, int ndims, const int *dims, int *result);
530 static int convert_lat_lons(val_extract_arguments *arguments, nc_region *region);
531 static int process_variable(val_extract_arguments *arguments, nc_var *var);
532 static double process_scan_line_att(val_extract_arguments *arguments, nc_region *region, const char *name);
533 static double process_location_var(val_extract_arguments *arguments, nc_region *region, const char *name);
534 static int process_df_var_attrs(val_extract_arguments *arguments, shash *attributes, int ncid, int varid, bool prepend_path);
535 static int process_df_file_attrs(val_extract_arguments *arguments, nc_region *region);
536 static int get_var_attributes(val_extract_arguments *arguments, nc_var *var);
537 static void free_var(val_extract_arguments *arguments, nc_var *var);
538 static nc_var_stats generate_stats(const double *data, int offset, int length);
539 static void get_array_ranges(const double *sorted_data, int length, double start, double end, int *start_i, int *end_i);
540 
541 // should go into a math lib
542 static unsigned digits(long long n);
543 
544 static int *nc_inq_grps_recursive(int ncid, int *gids, int *ngrps, size_t *ngrps_max);
545 static unsigned nc_get_sizeof(nc_type xtype);
546 
547 #define clean_and_return(ret) do { \
548  if ((ret) == VALEXTRACT_ERR_NONE){ \
549  arguments->val_extract_parser(VALEXTRACT_KEY_SUCCESS, &region, arguments->user_input); \
550  } else { \
551  arguments->val_extract_parser(VALEXTRACT_KEY_ERROR, &region, arguments->user_input); \
552  } \
553  nc_close(ncid); \
554  arguments->val_extract_parser(VALEXTRACT_KEY_FINI, &region, arguments->user_input); \
555  if (all_vars){ \
556  for (product_count--; product_count >= 0; product_count--){ \
557  free_var(arguments, all_vars[product_count]); \
558  free(all_vars[product_count]); \
559  } \
560  free(all_vars); \
561  } \
562  if (region.boxes){ \
563  free(region.boxes); \
564  } \
565  if (region.flag_counts){ \
566  free(region.flag_counts); \
567  } \
568  if (region.pixel_flags){ \
569  free(region.pixel_flags); \
570  } \
571  if (file.flag_masks){ \
572  free(file.flag_masks); \
573  } \
574  if (file.flag_string){ \
575  free(file.flag_string); \
576  } \
577  if (file.flag_meanings){ \
578  free(file.flag_meanings); \
579  } \
580  if (file.attributes){ \
581  shash_destroy(file.attributes); \
582  } \
583  if (file.gids){ \
584  free(file.gids); \
585  } \
586  return (ret); \
587 } while (0)
588 
590  if (!arguments) {
591  return VALEXTRACT_ERR_INPUT;
592  }
593  if (arguments->log == NULL) {
594  arguments->log = olog_load_default();
595  }
596  if (!arguments->val_extract_parser) {
597  olog_crit(arguments->log, "No parser given, bailing out\n");
598  return VALEXTRACT_ERR_INPUT;
599  }
600 
601  arguments->val_extract_parser(VALEXTRACT_KEY_INIT, NULL, arguments->user_input);
602 
603  if (_check_positioning(arguments)) {
604  return VALEXTRACT_ERR_INPUT;
605  }
606 // olog_debug(arguments->log, "ifile: %s\n", arguments->ifile);
607 
608  int ncid, nc_ret, i;
609  if ((nc_ret = nc_open(arguments->ifile, NC_NOWRITE, &ncid)) != NC_NOERR) {
610  olog_crit(arguments->log, "Error opening file, error %d\n", nc_ret);
612  }
613 
614  int ndims, nvars, ngatts, unlimdimid;
615  if ((nc_ret = nc_inq(ncid, &ndims, &nvars, &ngatts, &unlimdimid)) != NC_NOERR) {
616  olog_crit(arguments->log, "Error getting file information, error %d\n", nc_ret);
618  }
619 // olog_debug(arguments->log, "nc_inq: %d | %d | %d | %d\n", ndims, nvars, ngatts, unlimdimid);
620 
621  char dim_name[64];
622  int dim_lengths[ndims];
623  int line_count = -1, pixel_count = -1;
624  int line_dimid = -1, pixel_dimid = -1, pixel_control_dimid = -1;
625  for (i = 0; i < ndims; i++) {
626  size_t dim_length;
627  if ((nc_ret = nc_inq_dim(ncid, i, dim_name, &dim_length)) != NC_NOERR) {
628  olog_crit(arguments->log, "Error getting dimensions, error %d\n", nc_ret);
630  }
631 // olog_debug(arguments->log, "nc_inq_dim: %d | %zd | %s\n", i, dim_length, dim_name);
632  dim_lengths[i] = dim_length;
633  if ((strcmp(dim_name, "number_of_lines") == 0) || (strcmp(dim_name, "Number_of_Scan_Lines") == 0)) {
634  line_count = dim_length;
635  line_dimid = i;
636  } else if ((strcmp(dim_name, "pixels_per_line") == 0) || (strcmp(dim_name, "Pixels_per_Scan_Line") == 0)) {
637  pixel_count = dim_length;
638  pixel_dimid = i;
639  } else if ((strcmp(dim_name, "pixel_control_points") == 0) || (strcmp(dim_name, "Number_of_Pixel_Control_Points") == 0)) {
640  pixel_control_dimid = i;
641  }
642  }
643 
644 // olog_debug(arguments->log, "line dim: %d, pixel dim: %d\n", line_count, pixel_count);
645  if (line_count < 0 || pixel_count < 0) {
646  olog_crit(arguments->log, "Could not identify line/pixel dims, exiting\n");
648  }
649 
650  size_t ngrps_max = 16;
651  int *gids = malloc(sizeof(int) * ngrps_max), ngrps = 0;
652  gids[ngrps++] = ncid;
653  gids = nc_inq_grps_recursive(ncid, gids, &ngrps, &ngrps_max);
654 
655  nc_file file = {
656  .file_path = arguments->ifile,
657  .ndims = ndims, .dim_lengths = dim_lengths,
658  .ngrps = ngrps, .gids = gids,
659  .line_dimid = line_dimid, .pixel_dimid = pixel_dimid, .pixel_control_dimid = pixel_control_dimid,
660  .control_point_size = dim_lengths[pixel_control_dimid] / dim_lengths[pixel_dimid],
661  .attributes = NULL
662  };
663  nc_region region = {
664  .file = &file,
665  .box_count = 0
666  };
667  int product_count = 0;
668  nc_var **all_vars = NULL;
669 
670  if (arguments->lat_and_lon) {
671  if (arguments->start_lat == -90. && arguments->end_lat == 90. && arguments->start_lon == -180. && arguments->end_lon == 180.) {
672  region.box_count = 1;
673  region.boxes = malloc(sizeof(nc_box));
674  if (region.boxes == NULL) {
675  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
677  }
678  region.boxes[0].start[0] = 0;
679  region.boxes[0].start[1] = 0;
680  region.boxes[0].count[0] = line_count;
681  region.boxes[0].count[1] = pixel_count;
682 
683  nc_point center_pixel = {
684  .line = line_count >> 1,
685  .pixel = pixel_count >> 1
686  };
687  region.center = center_pixel;
688  } else {
689  int convert_err = convert_lat_lons(arguments, &region);
690  if (convert_err) {
691  if (convert_err != VALEXTRACT_ERR_POINT_NOT_FOUND) {
692  convert_err = VALEXTRACT_ERR_NCFILE_INVALID;
693  }
694  clean_and_return(convert_err);
695  }
696  }
697  } else {
698  int start_line, end_line, start_pixel, end_pixel;
699  start_line = end_line = start_pixel = end_pixel = 0;
700  if (arguments->box_size > 0 || arguments->box_size_km > 0) {
701  nc_point center_pixel = {
702  .line = arguments->start_line,
703  .pixel = arguments->start_pixel
704  };
705  region.center = center_pixel;
706 
707  if (arguments->box_size > 0) {
708  int geo_box_margin = arguments->box_size >> 1; // if odd, -1, then divide by 2; else divide by 2
709  start_line = arguments->start_line - geo_box_margin;
710  end_line = arguments->start_line + geo_box_margin;
711  start_pixel = arguments->start_pixel - (geo_box_margin * file.control_point_size);
712  end_pixel = arguments->start_pixel + (geo_box_margin * file.control_point_size);
713  } else {
714  int convert_err = convert_lat_lons(arguments, &region);
715  if (convert_err) {
716  if (convert_err != VALEXTRACT_ERR_POINT_NOT_FOUND) {
717  convert_err = VALEXTRACT_ERR_NCFILE_INVALID;
718  }
719  clean_and_return(convert_err);
720  }
721  }
722  } else {
723  nc_point center_pixel = {
724  .line = (arguments->start_line + arguments->end_line) >> 1,
725  .pixel = (arguments->start_pixel + arguments->end_pixel) >> 1
726  };
727  region.center = center_pixel;
728 
729  start_line = arguments->start_line;
730  end_line = arguments->end_line;
731  start_pixel = arguments->start_pixel;
732  end_pixel = arguments->end_pixel;
733  }
734 
735  if (arguments->box_size_km <= 0) {
736  if (start_line >= line_count) {
737  olog_crit(arguments->log, "sline %d >= line count (%d)\n", start_line, line_count);
739  }
740  if (start_pixel >= pixel_count) {
741  olog_crit(arguments->log, "spixl %d >= pixel count (%d)\n", start_pixel, pixel_count);
743  }
744  if (start_line < 0) {
745  olog_notice(arguments->log, "sline %d < 0; adjusting\n", start_line);
746  start_line = 0;
747  }
748  if (start_pixel < 0) {
749  olog_notice(arguments->log, "spixl %d < 0; adjusting\n", start_pixel);
750  start_pixel = 0;
751  }
752  if (end_line >= line_count) {
753  olog_notice(arguments->log, "eline %d >= line count (%d); adjusting\n", end_line, line_count);
754  end_line = line_count - 1;
755  }
756  if (end_pixel >= pixel_count) {
757  olog_notice(arguments->log, "epixl %d >= pixel count (%d); adjusting\n", end_pixel, pixel_count);
758  end_pixel = pixel_count - 1;
759  }
760 
761  region.box_count = 1;
762  region.boxes = malloc(sizeof(nc_box));
763  if (region.boxes == NULL) {
764  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
766  }
767  region.boxes[0].start[0] = start_line;
768  region.boxes[0].start[1] = start_pixel;
769  region.boxes[0].count[0] = end_line - start_line + 1;
770  region.boxes[0].count[1] = end_pixel - start_pixel + 1;
771  }
772  }
773 
774  if (region.box_count == 0) {
775  olog_crit(arguments->log, "Point not found in input file\n");
777  }
778 
779  if ((nc_ret = process_df_file_attrs(arguments, &region)) != 0) {
780  if (nc_ret == VALEXTRACT_CMD_STOP) {
781  nc_ret = 0;
782  }
783  clean_and_return(nc_ret);
784  }
785 
786  if (arguments->product_count) {
787  product_count = arguments->product_count;
788  } else {
789  int gid_i;
790  for (gid_i = 0; gid_i < ngrps; gid_i++) {
791  int g_nvars;
792  if ((nc_ret = nc_inq_varids(gids[gid_i], &g_nvars, NULL)) != NC_NOERR) {
793  olog_crit(arguments->log, "Error finding variable count, error %d\n", nc_ret);
795  }
796  product_count += g_nvars;
797  }
798  }
799  all_vars = malloc(product_count * sizeof(nc_var));
800  product_count = 0;
801 
802  if (arguments->product_count) {
803  for (i = 0; i < arguments->product_count; i++) {
804  int varid, gid, gid_i;
805  varid = gid = -1;
806  for (gid_i = 0; gid_i < ngrps; gid_i++) {
807  if ((nc_ret = nc_inq_varid(gids[gid_i], arguments->products[i], &varid)) == NC_NOERR) {
808  gid = gids[gid_i];
809  break;
810  }
811  }
812  if (gid != -1 && varid != -1) {
813 // olog_debug(arguments->log, "%s, gid = %d, varid = %d, nc_ret = %d\n", arguments->products[i], gid, varid, nc_ret);
814  nc_var *var = malloc(sizeof(nc_var));
815  var->file = &file;
816  var->region = &region;
817  var->name = arguments->products[i];
818  var->gid = gid;
819  var->varid = varid;
820  if (get_var_attributes(arguments, var)) {
822  } else {
823  nc_ret = process_variable(arguments, var);
824  free_var(arguments, var);
825  free(var);
826  if (nc_ret) {
827  if (nc_ret == VALEXTRACT_CMD_STOP) {
828  nc_ret = 0;
829  }
831  }
832 // all_vars[product_count++] = var;
833  }
834  } else {
835  olog_crit(arguments->log, "Variable %s not found\n", arguments->products[i]);
837  }
838  }
839  } else {
840  int gid_i;
841  for (gid_i = 0; gid_i < ngrps; gid_i++) {
842  int g_nvars;
843  if ((nc_ret = nc_inq_varids(gids[gid_i], &g_nvars, NULL)) != NC_NOERR) {
844  olog_crit(arguments->log, "Error finding variable count, error %d\n", nc_ret);
846  }
847  int varids[g_nvars];
848  if ((nc_ret = nc_inq_varids(gids[gid_i], &g_nvars, varids)) != NC_NOERR) {
849  olog_crit(arguments->log, "Error finding variables, error %d\n", nc_ret);
851  }
852 // olog_debug(arguments->log, "gid %d, variable count: %d\n", gids[gid_i], g_nvars);
853  for (i = 0; i < g_nvars; i++) {
854  nc_var *var = malloc(sizeof(nc_var));
855  var->file = &file;
856  var->region = &region;
857  var->gid = gids[gid_i];
858  var->varid = varids[i];
859  if (get_var_attributes(arguments, var)) {
861  } else {
862  nc_ret = process_variable(arguments, var);
863  free_var(arguments, var);
864  free(var);
865  if (nc_ret) {
866  if (nc_ret == VALEXTRACT_CMD_STOP) {
867  nc_ret = 0;
868  }
870  }
871 // all_vars[product_count++] = var;
872  }
873  }
874  }
875  }
876 
877 // olog_debug(arguments->log, "Exiting... ");
878  print_time();
880 }
881 
882 static int *nc_inq_grps_recursive(int ncid, int *gids, int *ngrps, size_t *ngrps_max) {
883  int my_ngrps = 0, ngrps_was = *ngrps, nc_ret;
884  if ((nc_ret = nc_inq_grps(ncid, &my_ngrps, NULL)) != NC_NOERR) {
885  return NULL;
886  }
887  if ((unsigned) (*ngrps + my_ngrps) >= *ngrps_max) {
888  *ngrps_max *= 2;
889  int *new_gids = realloc(gids, sizeof(int) * *ngrps_max);
890  if (new_gids != NULL) {
891  gids = new_gids;
892  } else {
893  return NULL;
894  }
895  }
896  if ((nc_ret = nc_inq_grps(ncid, &my_ngrps, gids + *ngrps)) != NC_NOERR) {
897  return NULL;
898  }
899  *ngrps += my_ngrps;
900  int gid_i, my_ngrps_max = *ngrps;
901  for (gid_i = ngrps_was; gid_i < my_ngrps_max; gid_i++) {
902  gids = nc_inq_grps_recursive(gids[gid_i], gids, ngrps, ngrps_max);
903  }
904  return gids;
905 }
906 
907 static uint32_t find_closest_index(double target_lat, double target_lon, uint32_t length, double *lats, double *lons) {
908  uint32_t i = 0, closest_i = -1;
909  double best_diff = DBL_MAX;
910  for (; i < length; i++) {
911  double diff = sqrt(pow(lats[i] - target_lat, 2) + pow(lons[i] - target_lon, 2));
912  if (diff < best_diff) {
913  closest_i = i;
914  best_diff = diff;
915  }
916  }
917 // olog_debug(arguments->log, "Closest diff: %f, index %d\n", best_diff, closest_i);
918  return closest_i;
919 }
920 
921 #if 0
922 const long double pi = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899L;
923 static double rad2deg(const double rad) {
924  return (180.0 * rad / (pi));
925 }
926 static double deg2rad(const double deg) {
927  return (pi * deg / 180.0);
928 }
929 static long double rad2degl(const long double rad) {
930  return (180.0 * rad / (pi));
931 }
932 static long double deg2radl(const long double deg) {
933  return (pi * deg / 180.0);
934 }
935 
936 static uint32_t find_closest_index_polar(double target_lat, double target_lon, uint32_t length, double *lats, double *lons) {
937  double target_x = cos(deg2rad(target_lat)) * cos(deg2rad(target_lon));
938  double target_y = cos(deg2rad(target_lat)) * sin(deg2rad(target_lon));
939  double target_z = sin(deg2rad(target_lat));
940 
941  uint32_t i = 0, closest_i = -1;
942  double best_diff = DBL_MAX;
943  for (;i<length;i++) {
944  double this_x = cos(deg2rad(lats[i])) * cos(deg2rad(lons[i]));
945  double this_y = cos(deg2rad(lats[i])) * sin(deg2rad(lons[i]));
946  double this_z = sin(deg2rad(lats[i]));
947 
948  double diff = this_x * target_x + this_y * target_y + this_z * target_z;
949  if (diff > best_diff) {
950  closest_i = i;
951  best_diff = diff;
952  }
953  }
954  return closest_i;
955 }
956 #endif
957 
958 static nc_box *add_to_boxes(nc_box *boxes, unsigned *box_shift, int *box_i, int *box_max, int line_i, int start_pixel, int end_pixel) {
959  if (*box_i == *box_max) {
960  nc_box *new_boxes_ptr = realloc(boxes, sizeof(nc_box) * (1 << (++*box_shift)));
961  if (new_boxes_ptr == NULL) {
962  return NULL;
963  }
964  boxes = new_boxes_ptr;
965  *box_max = (1 << *box_shift) - 1;
966  }
967  nc_box new_box = { .start = { line_i, start_pixel }, .count = { 1, end_pixel - start_pixel + 1 } };
968  boxes[(*box_i)++] = new_box;
969  return boxes;
970 }
971 
972 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
973 static bool polygon_contains(const double *poly_x, const double *poly_y, const int poly_n, const double x, const double y) {
974  int i, j, c = 0;
975  for (i = 0, j = poly_n - 1; i < poly_n; j = i++) {
976  if (((poly_x[i] > x) != (poly_x[j] > x)) && (y < (poly_y[j] - poly_y[i]) * (x - poly_x[i]) / (poly_x[j] - poly_x[i]) + poly_y[i])) {
977  c = !c;
978  }
979  }
980  return c;
981 }
982 #if 0
983 // https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
984 static void polygon_center(double *poly_x, double *poly_y, const int poly_n, double *x, double *y) {
985  if (poly_n == 0) {
986  *x = *y = 0;
987  } else if (poly_n == 1) {
988  *x = poly_x[0];
989  *y = poly_y[0];
990  } else if (poly_n == 2) {
991  *x = (poly_x[0] + poly_x[1]) / 2;
992  *y = (poly_y[0] + poly_y[1]) / 2;
993  } else {
994  int i;
995  double area = 0;
996  *x = *y = 0;
997  for (i=0;i<poly_n;i++) {
998  double diff = (poly_x[i] * poly_y[i+1]) - (poly_x[i+1] * poly_y[i]);
999  *x += (poly_x[i] + poly_x[i+1]) * diff;
1000  *y += (poly_y[i] + poly_y[i+1]) * diff;
1001  area += diff;
1002  }
1003  if (area) {
1004  area /= 2;
1005  *x = *x / (6 * area);
1006  *y = *y / (6 * area);
1007  }
1008  }
1009 }
1010 #endif
1011 
1012 #if 0
1013 // DBSCAN is a relatively simple algorithm to detect clusters of points. It's no longer used here due to requiring a density
1014 // factor that would either need a look up table or a bunch of extra analysis to find it first. The oddities documented
1015 // for OPTICS below regarding neighbors apply to this algorithm, as well.
1016 struct dbscan {
1017  double x, y;
1018  bool noise, visited;
1019  int cluster;
1020  int neighbors[8], neighbor_count;
1021 };
1022 
1023 static void dbscan_expand(struct dbscan *db, struct dbscan *p, const int cluster, const double epsilon, const int min_points) {
1024  p->cluster = cluster;
1025  int neighbor_i;
1026  for (neighbor_i=0;neighbor_i<p->neighbor_count;neighbor_i++) {
1027  struct dbscan *neighbor = &db[p->neighbors[neighbor_i]];
1028  if (!neighbor->visited) {
1029  neighbor->visited = true;
1030  if (neighbor->neighbor_count >= min_points) {
1031  dbscan_expand(db, neighbor, cluster, epsilon, min_points);
1032  }
1033  }
1034  if (!neighbor->cluster) {
1035  neighbor->cluster = cluster;
1036  }
1037  }
1038 }
1039 
1040 static void dbscan(struct dbscan *db, const int db_count, const double epsilon, const int min_points) {
1041  int db_i, cluster = 0;
1042  for (db_i=0;db_i<db_count;db_i++) {
1043  db[db_i].noise = false;
1044  db[db_i].visited = false;
1045  db[db_i].cluster = 0;
1046  }
1047  for (db_i=0;db_i<db_count;db_i++) {
1048  if (!db[db_i].visited) {
1049  db[db_i].visited = true;
1050  if (db[db_i].neighbor_count < min_points) {
1051  db[db_i].noise = true;
1052  } else {
1053  dbscan_expand(db, &db[db_i], ++cluster, epsilon, min_points);
1054  }
1055  }
1056  }
1057 }
1058 #endif
1059 
1060 //https://en.wikipedia.org/wiki/OPTICS_algorithm
1061 // OPTICS is an algorithm to identify clusters of points in 2D space. The advantage of it is that it's
1062 // insensitive of cluster density, which is why it was chosen (pixel resolution = density). The reason
1063 // it's needed at all is because of gaps in data, such as when the SeaWiFS sensor switches tilt. When
1064 // that happens, a naive pixel grab could group pixels thousands of kilometers apart.
1065 //
1066 // Some weirdness that should be documented:
1067 // Epsilon (eps, in Wikipedia's pseudo-code) isn't even used. I don't even use a getNeighbors function.
1068 // I already know the neighbors because it's just a grid, so each point just has all of its gridly
1069 // neighbors in its neighbor list. This is slightly incorrect because I'm using this to detect gaps and the
1070 // appropriate epsilon would simply discard them as neighbors. This entire bit is used specifically because
1071 // I can't (easily) know the appropriate epsilon; one might be able to use sensor lookup tables and geospatial
1072 // geometry to calculate the correct grid size and, therefore, epsilon. To keep this library as generic as
1073 // possible, I use OPTICS's density-independent concept and a maximum ratio to determine if a pixel is too
1074 // far away from the core and should be considered a new cluster. If a reachability-distance is X times
1075 // larger than the current cluster's moving average, it's a new cluster. For these reasons, and any
1076 // assumptions I've made for this specific application, the struct for the data point is a kind of weird
1077 // format (for convenience), so this algorithm isn't in a nice, generic library like it should be (nor do I
1078 // plan to move it to its own library).
1079 struct optic {
1080  double x, y;
1084  unsigned i, j; // not part of the algorithm
1085 };
1086 
1087 static double optics_core_distance(const struct optic *db, const struct optic *p, const int min_points) {
1088  if (p->i == UINT_MAX || p->neighbor_count < min_points) {
1089  return DBL_MIN;
1090  }
1091  double distances[p->neighbor_count];
1092 
1093  int i;
1094  for (i = 0; i < p->neighbor_count; i++) {
1095  const struct optic *neighbor = &db[p->neighbors[i]];
1096 // distances[i] = sqrt(pow(p->x - neighbor->x, 2) + pow(p->y - neighbor->y, 2));
1097  distances[i] = vincenty_distance(p->x, p->y, neighbor->x, neighbor->y);
1098  }
1099  qsort(distances, p->neighbor_count, sizeof(double), compare_double);
1100  if (min_points <= p->neighbor_count) {
1101  return distances[min_points - 1];
1102  } else {
1103  return distances[p->neighbor_count - 1];
1104  }
1105 }
1106 static void optics_update(struct optic *db, struct optic *p, const int min_points, pqueue *seeds) {
1107  int i;
1108  for (i = 0; i < p->neighbor_count; i++) {
1109  struct optic *neighbor = &db[p->neighbors[i]];
1110  if (!neighbor->processed) {
1111 // double dist_to_neighbor = sqrt(pow(p->x - neighbor->x, 2) + pow(p->y - neighbor->y, 2));
1112  const double dist_to_neighbor = vincenty_distance(p->x, p->y, neighbor->x, neighbor->y);
1113  const double reachability = (dist_to_neighbor > p->core ? dist_to_neighbor : p->core);
1114  if (neighbor->reachability == DBL_MIN) {
1115  pqueue_push(seeds, neighbor->reachability = reachability, neighbor);
1116  } else if (reachability < neighbor->reachability) {
1117  pqueue_repush(seeds, neighbor->reachability = reachability, neighbor);
1118  }
1119  }
1120  }
1121 }
1122 static void optics(struct optic *db, const int db_count, const int min_points, struct optic **out_db) {
1123  int db_i;
1124  for (db_i = 0; db_i < db_count; db_i++) {
1125  db[db_i].processed = false;
1126  db[db_i].reachability = db[db_i].core = DBL_MIN;
1127  }
1128  pqueue *seeds = pqueue_create();
1129  int out_db_i = 0;
1130  for (db_i = 0; db_i < db_count; db_i++) {
1131  if (!db[db_i].processed) {
1132  db[db_i].processed = true;
1133  out_db[out_db_i++] = &db[db_i];
1134  if ((db[db_i].core = optics_core_distance(db, &db[db_i], min_points)) != DBL_MIN) {
1135  optics_update(db, &db[db_i], min_points, seeds);
1136  struct optic *seed = NULL;
1137  while ((seed = pqueue_pull(seeds)) != NULL) {
1138  if (!seed->processed) {
1139  seed->processed = true;
1140  out_db[out_db_i++] = seed;
1141  if ((db[db_i].core = optics_core_distance(db, seed, min_points)) != DBL_MIN) {
1142  optics_update(db, seed, min_points, seeds);
1143  }
1144  }
1145  }
1146  }
1147  }
1148  }
1149  pqueue_destroy(seeds);
1150 }
1151 
1152 // This assumes the latitude and longitude variables do not have any scaling or offset and that the
1153 // fill value is -999. The fill value is currently listed incorrectly in the data files, so it's a
1154 // moot point to do it correctly here. I only check the latitude array for -999; if one is -999,
1155 // then they both are.
1156 // Due to pre-screening for NAVFAILed pixels, as mentioned above, NAVFAIL will never actually be reported.
1157 static int convert_lat_lons(val_extract_arguments *arguments, nc_region *region) {
1158  nc_file *file = region->file;
1159  size_t latlon_length = file->dim_lengths[file->line_dimid] * file->dim_lengths[file->pixel_dimid];
1160  double *lats = malloc(sizeof(double) * latlon_length);
1161  if (lats == NULL) {
1162  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1163  return -1;
1164  }
1165  double *lons = malloc(sizeof(double) * latlon_length);
1166  if (lons == NULL) {
1167  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1168  free(lats);
1169  return -1;
1170  }
1171 
1172  bool found_lat, found_lon;
1173  found_lat = found_lon = false;
1174  int nc_ret, varid, gid_i;
1175  varid = -1;
1176  for (gid_i = 0; gid_i < file->ngrps && !(found_lat && found_lon); gid_i++) {
1177  if (!found_lat && (nc_ret = nc_inq_varid(file->gids[gid_i], "latitude", &varid)) == NC_NOERR) {
1178  nc_get_var_double(file->gids[gid_i], varid, lats);
1179  found_lat = true;
1180  }
1181  if (!found_lon && (nc_ret = nc_inq_varid(file->gids[gid_i], "longitude", &varid)) == NC_NOERR) {
1182  nc_get_var_double(file->gids[gid_i], varid, lons);
1183  found_lon = true;
1184  }
1185  }
1186  if (!found_lat || !found_lon) {
1187  olog_crit(arguments->log, "Failed finding latitude and/or longitude variable");
1188  free(lats);
1189  free(lons);
1190  return -1;
1191  }
1192 
1193  if (arguments->box_size > 0 || arguments->box_size_km > 0) {
1194  int i;
1195  const int dims[2] = { file->dim_lengths[file->line_dimid], file->dim_lengths[file->pixel_dimid] };
1196  int dim_multipliers[2];
1197  calc_dim_multipliers(2, dims, dim_multipliers);
1198 
1199 // olog_debug(arguments->log, "Closest pixel: (%zd, %zd)\n", region->center.line, region->center.pixel);
1200  int closest_index;
1201  double distance_to_closest, arguments_lat, arguments_lon;
1202  if (arguments->lat_and_lon) {
1203  int result[2];
1204  unflatten_index(closest_index = find_closest_index(arguments->start_lat, arguments->start_lon, latlon_length, lats, lons), 2, dims, result);
1205  arguments->start_line = result[0];
1206  arguments->start_pixel = result[1];
1207  nc_point center_pixel = { .line = result[0], .pixel = result[1] };
1208  region->center = center_pixel;
1209  distance_to_closest = sqrt(pow(lats[closest_index] - arguments->start_lat, 2) + pow(lons[closest_index] - arguments->start_lon, 2));
1210  arguments_lat = arguments->start_lat;
1211  arguments_lon = arguments->start_lon;
1212  } else {
1213  closest_index = region->center.line * file->dim_lengths[file->pixel_dimid] + region->center.pixel;
1214  distance_to_closest = 0;
1215  arguments_lat = lats[closest_index];
1216  arguments_lon = lons[closest_index];
1217  }
1218 
1219 // olog_debug(arguments->log, "Closest pixel: %d (%zd, %zd), %f degrees away\n", closest_index, region->center.line, region->center.pixel, distance_to_closest);
1220 
1221  if (arguments->box_size > 0) {
1222  unsigned half_box = arguments->box_size >> 1;
1223  int line_start = region->center.line - half_box;
1224  int line_end = region->center.line + half_box;
1225  int pixel_start = region->center.pixel - half_box;
1226  int pixel_end = region->center.pixel + half_box;
1227  if (line_start < 0) {
1228  line_start = 0;
1229  }
1230  if (line_end >= dims[0]) {
1231  line_end = dims[0] - 1;
1232  }
1233  if (pixel_start < 0) {
1234  pixel_start = 0;
1235  }
1236  if (pixel_end >= dims[1]) {
1237  pixel_end = dims[1] - 1;
1238  }
1239  const int pixel_count = pixel_end - pixel_start + 1;
1240  const int line_count = line_end - line_start + 1;
1241  double **grid_lats = malloc(sizeof(double*) * line_count);
1242  double **grid_lons = malloc(sizeof(double*) * line_count);
1243  int **grid_cluster = malloc(sizeof(int*) * line_count);
1244  for (i = 0; i < line_count; i++) {
1245  grid_lats[i] = malloc(sizeof(double) * pixel_count);
1246  grid_lons[i] = malloc(sizeof(double) * pixel_count);
1247  grid_cluster[i] = malloc(sizeof(int) * pixel_count);
1248  }
1249 
1250  double dateline_offset = copysign(360, lons[closest_index]);
1251  int j;
1252  for (i = 0; i < line_count; i++) {
1253  int coords[2] = { line_start + i, pixel_start };
1254  int index = flatten_index(2, dim_multipliers, coords);
1255  for (j = 0; j < pixel_count; j++) {
1256  grid_lats[i][j] = lats[index + j];
1257  grid_lons[i][j] = lons[index + j];
1258  grid_cluster[i][j] = -1;
1259  if ((int) grid_lats[i][j] != -999 && copysign(1, grid_lons[i][j]) != copysign(1, lons[closest_index])) {
1260  if (fabs(grid_lons[i][j] - lons[closest_index]) > fabs(grid_lons[i][j] + dateline_offset - lons[closest_index])) {
1261  grid_lons[i][j] += dateline_offset;
1262  }
1263  }
1264  }
1265  }
1266 
1267  int closest_line = -1, closest_pixel = -1;
1268  struct optic *db = malloc(sizeof(struct optic) * line_count * pixel_count);
1269  struct optic **out_db = malloc(sizeof(struct optic*) * line_count * pixel_count);
1270  int db_i = 0;
1271  for (i = 0; i < line_count; i++) {
1272  for (j = 0; j < pixel_count; j++, db_i++) {
1273  if ((int) grid_lats[i][j] != -999) {
1274  db[db_i].i = i;
1275  db[db_i].j = j;
1276  db[db_i].x = grid_lats[i][j];
1277  db[db_i].y = grid_lons[i][j];
1278  if (grid_lats[i][j] == lats[closest_index] && grid_lons[i][j] == lons[closest_index]) {
1279  closest_line = i;
1280  closest_pixel = j;
1281  }
1282  int neighbor_i = 0;
1283  int n_i, n_j;
1284  for (n_i = i > 0 ? i - 1 : i; n_i <= (i + 1) && n_i < line_count; n_i++) {
1285  for (n_j = j > 0 ? j - 1 : j; n_j <= (j + 1) && n_j < pixel_count; n_j++) {
1286  if ((int) grid_lats[n_i][n_j] != -999 && (i != n_i || j != n_j)) {
1287  db[db_i].neighbors[neighbor_i++] = (n_i * pixel_count) + n_j;
1288  }
1289  }
1290  }
1291  db[db_i].neighbor_count = neighbor_i;
1292  } else {
1293  db[db_i].i = UINT_MAX;
1294  }
1295  }
1296  }
1297 
1298  if (closest_line == -1 || closest_pixel == -1) {
1299  olog_crit(arguments->log, "Error finding closest pixel, somehow\n");
1300  for (i = 0; i < line_count; i++) {
1301  free(grid_lats[i]);
1302  free(grid_lons[i]);
1303  free(grid_cluster[i]);
1304  }
1305  free(grid_lats);
1306  free(grid_lons);
1307  free(grid_cluster);
1308  free(lats);
1309  free(lons);
1310  free(db);
1311  free(out_db);
1312  return -1;
1313  }
1314 
1315  optics(db, db_i, 3, out_db);
1316 
1317  double current_total = out_db[0]->core;
1318  int current_count = 0;
1319 
1320  if (!arguments->optics_threshold) {
1321  arguments->optics_threshold = VALEXTRACT_OPTICS_DEFAULT;
1322  }
1323  db_i = 0;
1324  int cluster = 0;
1325  for (i = 0; i < line_count * pixel_count; i++, db_i++) {
1326  if (out_db[db_i]->i != UINT_MAX) {
1327  if (current_count && current_total > 1e-10 && (out_db[db_i]->reachability / (current_total / current_count)) > arguments->optics_threshold) {
1328  olog_debug(arguments->log, "Gap detected, cluster %d\n", cluster);
1329  olog_debug(arguments->log, "\tcount %d, total %f, threshold %f > %f\n", current_count, current_total, (out_db[db_i]->reachability / (current_total / current_count)), arguments->optics_threshold);
1330  olog_debug(arguments->log, "\t(%f / (%f/%d)) > %f\n", out_db[db_i]->reachability, current_total, current_count, arguments->optics_threshold);
1331  cluster++;
1332  // Using the actual reachability would desensitize the detection because it would start off with too large of a total.
1333  // Using the previous average assumes that clusters have similar density. Assuming anything isn't ideal, but this
1334  // should hold up pretty well.
1335  current_total = current_total / current_count;
1336  current_count = 1;
1337  } else {
1338  current_total += out_db[db_i]->reachability;
1339  current_count++;
1340  }
1341  grid_cluster[out_db[db_i]->i][out_db[db_i]->j] = cluster;
1342  }
1343  }
1344  if (cluster) {
1345  olog_debug(arguments->log, "Gap detected, last cluster (%d)\n", cluster);
1346  olog_debug(arguments->log, "\tcount %d, total %f, threshold %f <= %f\n", current_count, current_total, (current_total / current_count), arguments->optics_threshold);
1347  olog_debug(arguments->log, "\t(%f/%d) <= %f\n", current_total, current_count, arguments->optics_threshold);
1348  }
1349  free(db);
1350  free(out_db);
1351 
1352  cluster = grid_cluster[closest_line][closest_pixel];
1353 
1354  double max_neighbor_distance = -1;
1355  for (i = 0; i < line_count; i++) {
1356  for (j = 0; j < pixel_count; j++) {
1357  if ((int) grid_lats[i][j] != -999 && grid_cluster[i][j] == cluster) {
1358  int n_i, n_j;
1359  for (n_i = i > 0 ? i - 1 : i; n_i <= (i + 1) && n_i < line_count; n_i++) {
1360  for (n_j = j > 0 ? j - 1 : j; n_j <= (j + 1) && n_j < pixel_count; n_j++) {
1361  if ((int) grid_lats[n_i][n_j] != -999 && (i != n_i || j != n_j) && grid_cluster[n_i][n_j] == cluster) {
1362  double neighbor_distance = sqrt(pow(grid_lats[i][j] - grid_lats[n_i][n_j], 2) + pow(grid_lons[i][j] - grid_lons[n_i][n_j], 2));
1363  if (neighbor_distance > max_neighbor_distance) {
1364  max_neighbor_distance = neighbor_distance;
1365  }
1366  }
1367  }
1368  }
1369  }
1370  }
1371  }
1372  if (max_neighbor_distance != -1 && distance_to_closest > max_neighbor_distance) {
1373  olog_crit(arguments->log, "Point not found in file, %f degrees (distance to closest pixel) > %f (farthest neighbor)\n", distance_to_closest, max_neighbor_distance);
1374  for (i = 0; i < line_count; i++) {
1375  free(grid_lats[i]);
1376  free(grid_lons[i]);
1377  free(grid_cluster[i]);
1378  }
1379  free(grid_lats);
1380  free(grid_lons);
1381  free(grid_cluster);
1382  free(lats);
1383  free(lons);
1385  } else {
1386 // olog_debug(arguments->log, "Point found in file, %f degrees (distance to closest pixel) <= %f (farthest neighbor)\n", distance_to_closest, max_neighbor_distance);
1387  }
1388 
1389  unsigned box_shift = 3;
1390  nc_box *boxes = malloc(sizeof(nc_box) << box_shift);
1391  if (boxes == NULL) {
1392  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1393  for (i = 0; i < line_count; i++) {
1394  free(grid_lats[i]);
1395  free(grid_lons[i]);
1396  free(grid_cluster[i]);
1397  }
1398  free(grid_lats);
1399  free(grid_lons);
1400  free(grid_cluster);
1401  free(lats);
1402  free(lons);
1403  return -1;
1404  }
1405  int box_max = (1 << box_shift) - 1;
1406  int box_i = 0;
1407 
1408  for (i = 0; i < line_count; i++) {
1409  for (j = 0; j < pixel_count; j++) {
1410  if (grid_cluster[i][j] == cluster) {
1411  int this_start_pixel = j;
1412  for (; j < pixel_count && grid_cluster[i][j] == cluster; j++)
1413  ;
1414  nc_box *new_box_ptr = add_to_boxes(boxes, &box_shift, &box_i, &box_max, i + line_start, this_start_pixel + pixel_start, j + pixel_start - 1);
1415  if (new_box_ptr == NULL) {
1416  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1417  for (i = 0; i < line_count; i++) {
1418  free(grid_lats[i]);
1419  free(grid_lons[i]);
1420  free(grid_cluster[i]);
1421  }
1422  free(grid_lats);
1423  free(grid_lons);
1424  free(grid_cluster);
1425  free(boxes);
1426  free(lats);
1427  free(lons);
1428  return -1;
1429  }
1430  boxes = new_box_ptr;
1431  }
1432  }
1433  }
1434 
1435  region->box_count = box_i;
1436  region->boxes = boxes;
1437 
1438  for (i = 0; i < line_count; i++) {
1439  free(grid_lats[i]);
1440  free(grid_lons[i]);
1441  free(grid_cluster[i]);
1442  }
1443  free(grid_lats);
1444  free(grid_lons);
1445  free(grid_cluster);
1446 
1447  } else { // box_size_km set
1448  unsigned box_shift = 3;
1449  nc_box *boxes = malloc(sizeof(nc_box) << box_shift);
1450  if (boxes == NULL) {
1451  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1452  free(lats);
1453  free(lons);
1454  return -1;
1455  }
1456  int box_max = (1 << box_shift) - 1;
1457  int box_i = 0;
1458 
1459  // make one-line wide strips of pixels (I call 'em boxes)
1460  // It might be worth pursuing finding the least amount of rectangles, which may significantly
1461  // reduce the calls to nc_get_vara_<type>. But, I'm lazy and uneducated.
1462  int pixel_i, line_i, latlon_i = 0;
1463  int start_pixel, end_pixel;
1464  const double max_distance = arguments->box_size_km * 1000;
1465  for (line_i = 0; line_i < file->dim_lengths[file->line_dimid]; line_i++) {
1466  start_pixel = end_pixel = -1;
1467  for (pixel_i = 0; pixel_i < file->dim_lengths[file->pixel_dimid]; pixel_i++, latlon_i++) {
1468  const double distance = vincenty_distance(arguments_lat, arguments_lon, lats[latlon_i], lons[latlon_i]);
1469 // printf("vincenty_distance(%f, %f, %f, %f) = %f\n", arguments_lat, arguments_lon, lats[latlon_i], lons[latlon_i], distance);
1470  if ((int) lats[latlon_i] != -999 && distance <= max_distance) {
1471  if (start_pixel == -1) {
1472  start_pixel = end_pixel = pixel_i;
1473  } else {
1474  end_pixel = pixel_i;
1475  }
1476  } else if (start_pixel != -1) {
1477  nc_box *new_box_ptr = add_to_boxes(boxes, &box_shift, &box_i, &box_max, line_i, start_pixel, end_pixel);
1478  if (new_box_ptr == NULL) {
1479  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1480  free(boxes);
1481  free(lats);
1482  free(lons);
1483  return -1;
1484  }
1485  boxes = new_box_ptr;
1486  start_pixel = end_pixel = -1;
1487  }
1488  }
1489  if (start_pixel != -1) {
1490  nc_box *new_box_ptr = add_to_boxes(boxes, &box_shift, &box_i, &box_max, line_i, start_pixel, end_pixel);
1491  if (new_box_ptr == NULL) {
1492  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1493  free(boxes);
1494  free(lats);
1495  free(lons);
1496  return -1;
1497  }
1498  boxes = new_box_ptr;
1499  }
1500  }
1501 
1502  if (box_i == 0) {
1503  olog_crit(arguments->log, "Region not found in input file\n");
1504  free(boxes);
1505  free(lats);
1506  free(lons);
1508  }
1509 
1510  region->box_count = box_i;
1511  region->boxes = boxes;
1512  }
1513 
1514  } else { // boxsize <= 0
1515 
1516  unsigned box_shift = 3;
1517  nc_box *boxes = malloc(sizeof(nc_box) << box_shift);
1518  if (boxes == NULL) {
1519  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1520  free(lats);
1521  free(lons);
1522  return -1;
1523  }
1524  int box_max = (1 << box_shift) - 1;
1525  int box_i = 0;
1526 
1527  // NW NE SE SW NW
1528  double poly_lats[] = {
1529  arguments->end_lat, arguments->end_lat,
1530  arguments->start_lat, arguments->start_lat, arguments->end_lat
1531  };
1532  double poly_lons[] = {
1533  arguments->start_lon, arguments->end_lon,
1534  arguments->end_lon, arguments->start_lon, arguments->start_lon,
1535  };
1536 
1537  // make one-line wide strips of pixels (I call 'em boxes)
1538  // It might be worth pursuing finding the least amount of rectangles, which may significantly
1539  // reduce the calls to nc_get_vara_<type>. But, I'm lazy and uneducated.
1540  int pixel_i, line_i, latlon_i = 0;
1541  int start_pixel, end_pixel;
1542  for (line_i = 0; line_i < file->dim_lengths[file->line_dimid]; line_i++) {
1543  start_pixel = end_pixel = -1;
1544  for (pixel_i = 0; pixel_i < file->dim_lengths[file->pixel_dimid]; pixel_i++, latlon_i++) {
1545  if ((int) lats[latlon_i] != -999 && polygon_contains(poly_lats, poly_lons, 5, lats[latlon_i], lons[latlon_i])) {
1546  if (start_pixel == -1) {
1547  start_pixel = end_pixel = pixel_i;
1548  } else {
1549  end_pixel = pixel_i;
1550  }
1551  } else if (start_pixel != -1) {
1552  nc_box *new_box_ptr = add_to_boxes(boxes, &box_shift, &box_i, &box_max, line_i, start_pixel, end_pixel);
1553  if (new_box_ptr == NULL) {
1554  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1555  free(boxes);
1556  free(lats);
1557  free(lons);
1558  return -1;
1559  }
1560  boxes = new_box_ptr;
1561  start_pixel = end_pixel = -1;
1562  }
1563  }
1564  if (start_pixel != -1) {
1565  nc_box *new_box_ptr = add_to_boxes(boxes, &box_shift, &box_i, &box_max, line_i, start_pixel, end_pixel);
1566  if (new_box_ptr == NULL) {
1567  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1568  free(boxes);
1569  free(lats);
1570  free(lons);
1571  return -1;
1572  }
1573  boxes = new_box_ptr;
1574  }
1575  }
1576 
1577  if (box_i == 0) {
1578  olog_crit(arguments->log, "Region not found in input file\n");
1579  free(boxes);
1580  free(lats);
1581  free(lons);
1583  }
1584 
1585  region->box_count = box_i;
1586  region->boxes = boxes;
1587 
1588  nc_box center_box = region->boxes[box_i / 2];
1589  nc_point center_pixel = {
1590  .line = center_box.start[0] + (center_box.count[0] / 2),
1591  .pixel = center_box.start[1] + (center_box.count[1] / 2)
1592  };
1593  region->center = center_pixel;
1594  }
1595 
1596  free(lats);
1597  free(lons);
1598  return 0;
1599 }
1600 
1601 static void free_var(val_extract_arguments *arguments, nc_var *var) {
1602  if (var) {
1603  if (arguments->product_count == 0) {
1604  free(var->name);
1605  }
1606  if (var->group_name) {
1607  free(var->group_name);
1608  }
1609  if (var->attributes) {
1610  shash_destroy(var->attributes);
1611  }
1612  if (var->data) {
1613  free(var->data);
1614  }
1615  if (var->valid_data){
1616  free(var->valid_data);
1617  }
1618  if (var->dim_ids) {
1619  free(var->dim_ids);
1620  }
1621  }
1622 }
1623 
1624 static int get_var_attributes(val_extract_arguments *arguments, nc_var *var) {
1625  int nc_ret;
1626  var->data = NULL;
1627  var->valid_data = NULL;
1628  var->attributes = NULL;
1629 
1630  if (arguments->product_count == 0) {
1631  if ((var->name = malloc(sizeof(char) * 64)) == NULL) {
1632  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1633  return -1;
1634  }
1635  if ((nc_ret = nc_inq_varname(var->gid, var->varid, var->name)) != NC_NOERR) {
1636  olog_crit(arguments->log, "Error getting variable name, error %d\n", nc_ret);
1637  free((void*) var->name);
1638  return -1;
1639  }
1640  }
1641 
1642  if ((nc_ret = nc_inq_vartype(var->gid, var->varid, &var->data_type)) != NC_NOERR) {
1643  olog_crit(arguments->log, "Error getting data type for %s, error %d\n", var->name, nc_ret);
1644  if (arguments->product_count == 0) {
1645  free((void*) var->name);
1646  }
1647  return -1;
1648  }
1649 
1650  if ((var->data_sizeof = nc_get_sizeof(var->data_type)) == 0) {
1651  free(var->dim_ids);
1652  if (arguments->product_count == 0) {
1653  free((void*) var->name);
1654  }
1655  return -1;
1656  }
1657 
1658  if ((nc_ret = nc_inq_varndims(var->gid, var->varid, &var->ndims)) != NC_NOERR) {
1659  olog_crit(arguments->log, "Error getting dimension count for %s, error %d\n", var->name, nc_ret);
1660  if (arguments->product_count == 0) {
1661  free((void*) var->name);
1662  }
1663  return -1;
1664  }
1665  if ((var->dim_ids = malloc(sizeof(int) * var->ndims)) == NULL) {
1666  olog_crit(arguments->log, "Error allocating memory in '%s', near line %d\n", __FILE__, __LINE__);
1667  if (arguments->product_count == 0) {
1668  free((void*) var->name);
1669  }
1670  return -1;
1671  }
1672  if ((nc_ret = nc_inq_vardimid(var->gid, var->varid, var->dim_ids)) != NC_NOERR) {
1673  olog_crit(arguments->log, "Error getting dimensions for %s, error %d\n", var->name, nc_ret);
1674  free(var->dim_ids);
1675  if (arguments->product_count == 0) {
1676  free((void*) var->name);
1677  }
1678  return -1;
1679  }
1680 
1681  var->is_geospatial = (var->ndims == 2 && var->dim_ids[0] == var->file->line_dimid && (var->dim_ids[1] == var->file->pixel_dimid || var->dim_ids[1] == var->file->pixel_control_dimid));
1682  var->uses_control_points = (var->ndims == 2 && var->dim_ids[1] == var->file->pixel_control_dimid);
1683 
1684  if ((nc_ret = nc_get_att_double(var->gid, var->varid, "scale_factor", &var->scale)) != NC_NOERR) {
1685  if ((nc_ret = nc_get_att_double(var->gid, var->varid, "slope", &var->scale)) != NC_NOERR) {
1686  var->has_scale = false;
1687  } else {
1688  var->has_scale = true;
1689  }
1690  } else {
1691  var->has_scale = true;
1692  }
1693  if ((nc_ret = nc_get_att_double(var->gid, var->varid, "add_offset", &var->offset)) != NC_NOERR) {
1694  if ((nc_ret = nc_get_att_double(var->gid, var->varid, "intercept", &var->offset)) != NC_NOERR) {
1695  var->has_offset = false;
1696  } else {
1697  var->has_offset = true;
1698  }
1699  } else {
1700  var->has_offset = true;
1701  }
1702  if ((nc_ret = nc_get_att_double(var->gid, var->varid, "_FillValue", &var->fill)) != NC_NOERR) {
1703  if ((nc_ret = nc_get_att_double(var->gid, var->varid, "bad_value_scaled", &var->fill)) != NC_NOERR) {
1704  var->has_fill = false;
1705  } else {
1706  var->has_fill = true;
1707  }
1708  } else {
1709  var->has_fill = true;
1710  }
1711 
1712  if (arguments->variable_atts) {
1713  if ((var->attributes = shash_create(0)) == NULL) {
1714  olog_crit(arguments->log, "Error creating variable attribute hash, %s\n", var->name);
1715  return VALEXTRACT_ERR_UNKNOWN;
1716  }
1717  if (process_df_var_attrs(arguments, var->attributes, var->gid, var->varid, false)) {
1718  olog_crit(arguments->log, "Error getting variable attributes, %s\n", var->name);
1719  return VALEXTRACT_ERR_UNKNOWN;
1720  }
1721  }
1722 
1723  char group_name_tmp[255];
1724  size_t grpname_len = 0;
1725  if ((nc_ret = nc_inq_grpname_full(var->gid, &grpname_len, group_name_tmp)) != NC_NOERR) {
1726  olog_crit(arguments->log, "Error getting group name: %d\n", nc_ret);
1727  return VALEXTRACT_ERR_UNKNOWN;
1728  }
1729  char *group_name = malloc((grpname_len + 1) * sizeof(char));
1730  strcpy(group_name, group_name_tmp + 1);
1731  group_name[grpname_len] = '\0';
1732  var->group_name = group_name;
1733 
1734  var->stats.initialized = false;
1735  var->filtered_stats.initialized = false;
1736  var->iqr_stats.initialized = false;
1737 
1738  return 0;
1739 }
1740 
1741 static int process_variable(val_extract_arguments *arguments, nc_var *var) {
1742  int nc_ret;
1743  nc_region *region = var->region;
1744  nc_file *file = var->file;
1745  int pixel_count = 0;
1746  bool skip_stats = false;
1747 
1748  for (int i=0;i<arguments->skip_stats_count;i++){
1749  if (!strcmp(var->name, arguments->skip_stats[i++])) {
1750  skip_stats = true;
1751  break;
1752  }
1753  }
1754 
1755  //TODO: fix region stuff for geospatial vars that use control points
1756  if (var->is_geospatial || !arguments->geospatial_only) {
1757  if (var->is_geospatial && arguments->geospatial_to_double) {
1758  var->data_type = NC_DOUBLE;
1759  var->data_sizeof = sizeof(double);
1760  }
1761  void *data;
1762  double *sorted_data = NULL;
1763  double center_value = 0;
1764  if (var->is_geospatial) {
1765 // pixel_count = nc_get_region_size(region);
1766  pixel_count = region->pixel_count;
1767  data = calloc(pixel_count, var->data_sizeof);
1768 
1769  if (arguments->geospatial_to_double) {
1770  if ((nc_ret = nc_get_varr_double(var->gid, var->varid, region, data)) != NC_NOERR) {
1771  olog_crit(arguments->log, "Error getting data area for %s, error %d\n", var->name, nc_ret);
1772  return -1;
1773  }
1774  } else {
1775  if ((nc_ret = nc_get_varr(var->gid, var->varid, region, data)) != NC_NOERR) {
1776  olog_crit(arguments->log, "Error getting data area for %s, error %d\n", var->name, nc_ret);
1777  return -1;
1778  }
1779  }
1780 
1781  if (!skip_stats){
1782  sorted_data = calloc(pixel_count, sizeof(double));
1783  if (var->data_type == NC_DOUBLE) {
1784  memcpy(sorted_data, data, pixel_count << 3);
1785  } else {
1786  if ((nc_ret = nc_get_varr_double(var->gid, var->varid, region, sorted_data)) != NC_NOERR) {
1787  olog_crit(arguments->log, "Error getting data area for %s, error %d\n", var->name, nc_ret);
1788  return -1;
1789  }
1790  }
1791  }
1792 // olog_debug(arguments->log, "Center pixel coords: %zd, %zd\n", region->center.line, region->center.pixel);
1793  size_t center_point[2] = { region->center.line, region->center.pixel };
1794  if ((nc_ret = nc_get_var1_double(var->gid, var->varid, center_point, &center_value)) != NC_NOERR) {
1795  olog_crit(arguments->log, "Error getting data center for %s, error %d\n", var->name, nc_ret);
1796  return -1;
1797  }
1798  if (!(var->has_fill && center_value == var->fill)) {
1799  if (var->has_scale || var->has_offset) {
1800  if (var->has_scale) {
1801  center_value *= var->scale;
1802  }
1803  if (var->has_offset) {
1804  center_value += var->offset;
1805  }
1806  }
1807  }
1808  var->stats.center_value = center_value;
1809  } else {
1810  var->stats.center_value = 0;
1811  if (var->ndims == 1) {
1812  if (var->dim_ids[0] == file->line_dimid) {
1813  pixel_count = nc_get_region_dim_size(region, 0);
1814  data = calloc(pixel_count, var->data_sizeof);
1815  if ((nc_ret = nc_get_varrd(var->gid, var->varid, region, 0, data)) != NC_NOERR) {
1816  olog_crit(arguments->log, "Error getting data for %s, error %d\n", var->name, nc_ret);
1817  return -1;
1818  }
1819  if (!skip_stats){
1820  sorted_data = calloc(pixel_count, sizeof(double));
1821  if ((nc_ret = nc_get_varrd_double(var->gid, var->varid, region, 0, sorted_data)) != NC_NOERR) {
1822  olog_crit(arguments->log, "Error getting data for %s, error %d\n", var->name, nc_ret);
1823  return -1;
1824  }
1825  }
1826  } else if (var->dim_ids[0] == file->pixel_dimid || var->dim_ids[0] == file->pixel_control_dimid) {
1827  pixel_count = nc_get_region_dim_size(region, 1);
1828  data = calloc(pixel_count, var->data_sizeof);
1829  if ((nc_ret = nc_get_varrd(var->gid, var->varid, region, 1, data)) != NC_NOERR) {
1830  olog_crit(arguments->log, "Error getting data for %s, error %d\n", var->name, nc_ret);
1831  return -1;
1832  }
1833  if (!skip_stats){
1834  sorted_data = calloc(pixel_count, sizeof(double));
1835  if ((nc_ret = nc_get_varrd_double(var->gid, var->varid, region, 1, sorted_data)) != NC_NOERR) {
1836  olog_crit(arguments->log, "Error getting data for %s, error %d\n", var->name, nc_ret);
1837  return -1;
1838  }
1839  }
1840  }
1841  }
1842  if (!pixel_count) {
1843  uint8_t i;
1844  pixel_count = 1;
1845  for (i = 0; i < var->ndims; i++) {
1846  pixel_count *= file->dim_lengths[var->dim_ids[i]];
1847  }
1848  data = calloc(pixel_count, var->data_sizeof);
1849  if ((nc_ret = nc_get_var(var->gid, var->varid, data)) != NC_NOERR) {
1850  olog_crit(arguments->log, "Error getting data for %s, error %d\n", var->name, nc_ret);
1851  return -1;
1852  }
1853  if (!skip_stats){
1854  sorted_data = calloc(pixel_count, sizeof(double));
1855  if ((nc_ret = nc_get_var_double(var->gid, var->varid, sorted_data)) != NC_NOERR) {
1856  olog_crit(arguments->log, "Error getting data for %s, error %d\n", var->name, nc_ret);
1857  return -1;
1858  }
1859  }
1860  }
1861  }
1862  var->data = data;
1863  var->valid_data = NULL;
1864 
1865  if (!skip_stats){
1866  var->valid_data = sorted_data;
1867 
1868  val_extract_valid_range *valid_range = NULL;
1869  int i;
1870  for (i = 0; i < arguments->valid_range_count && !valid_range; i++) {
1871  if (starts_with(var->name, arguments->valid_ranges[i].prefix)) {
1872  valid_range = &arguments->valid_ranges[i];
1873  }
1874  }
1875 
1876  int good_pixels = 0;
1877  for (i = 0; i < pixel_count; i++) {
1878  if (!(var->has_fill && sorted_data[i] == var->fill)) {
1879  double v = sorted_data[i];
1880  if (var->is_geospatial && (region->pixel_flags[i] & arguments->ignore_flags_mask)) {
1881  continue;
1882  }
1883  if (var->has_scale || var->has_offset) {
1884  if (var->has_scale) {
1885  v *= var->scale;
1886  }
1887  if (var->has_offset) {
1888  v += var->offset;
1889  }
1890 
1891  switch (var->data_type) {
1892  case NC_BYTE:
1893  ((int8_t*) data)[i] = (int8_t) v;
1894  break;
1895  case NC_CHAR:
1896  case NC_UBYTE:
1897  ((uint8_t*) data)[i] = (uint8_t) v;
1898  break;
1899  case NC_SHORT:
1900  ((int16_t*) data)[i] = (int16_t) v;
1901  break;
1902  case NC_USHORT:
1903  ((uint16_t*) data)[i] = (uint16_t) v;
1904  break;
1905  case NC_INT: // case NC_LONG:
1906  ((int32_t*) data)[i] = (int32_t) v;
1907  break;
1908  case NC_UINT:
1909  ((uint32_t*) data)[i] = (uint32_t) v;
1910  break;
1911  case NC_INT64:
1912  ((int64_t*) data)[i] = (int64_t) v;
1913  break;
1914  case NC_UINT64:
1915  ((uint64_t*) data)[i] = (uint64_t) v;
1916  break;
1917  case NC_FLOAT:
1918  ((float*) data)[i] = (float) v;
1919  break;
1920  case NC_DOUBLE:
1921  ((double*) data)[i] = (double) v;
1922  break;
1923  default:
1924  free(sorted_data);
1925  return -1;
1926  }
1927  }
1928 
1929  if (!valid_range || (v >= valid_range->min && v <= valid_range->max)) {
1930  sorted_data[good_pixels++] = v;
1931  }
1932  }
1933  }
1934 
1935  var->valid_data_count = good_pixels;
1936  if (good_pixels) {
1937  if (!var->is_geospatial) {
1938  center_value = sorted_data[good_pixels >> 1];
1939  }
1940  qsort(sorted_data, good_pixels, sizeof(double), compare_double);
1941 
1942  var->stats = generate_stats(sorted_data, 0, good_pixels);
1943  var->stats.center_value = center_value;
1944 
1945  int filter_start_i = -1, filter_end_i = -1;
1946  const double filter_start = var->stats.median - (1.5 * var->stats.stddev);
1947  const double filter_end = var->stats.median + (1.5 * var->stats.stddev);
1948  get_array_ranges(sorted_data, good_pixels, filter_start, filter_end, &filter_start_i, &filter_end_i);
1949  int filtered_length = filter_end_i - filter_start_i + 1;
1950  var->filtered_stats = generate_stats(sorted_data, filter_start_i, filtered_length);
1951 
1952  int iqr_start_i = -1, iqr_end_i = -1;
1953  const double iqr_start = sorted_data[(int) (((good_pixels + 1) / 4))];
1954  const double iqr_end = sorted_data[(int) (((good_pixels + 1) * 3 / 4) - 1)];
1955  get_array_ranges(sorted_data, good_pixels, iqr_start, iqr_end, &iqr_start_i, &iqr_end_i);
1956  int iqr_length = iqr_end_i - iqr_start_i + 1;
1957  if (iqr_length >= 8) {
1958  var->iqr_stats = generate_stats(sorted_data, iqr_start_i, iqr_length);
1959  var->iqr_stats.count = iqr_length;
1960  } else {
1961  nc_var_stats iqr_stats = {
1962  .initialized = true, .count = 0, .center_value = 0,
1963  .min = 0, .max = 0, .mean = 0, .median = 0, .stddev = 0, .rms = 0
1964  };
1965  var->iqr_stats = iqr_stats;
1966  }
1967  } else {
1968  nc_var_stats stats = {
1969  .initialized = true, .count = 0, .center_value = 0,
1970  .min = 0, .max = 0, .mean = 0, .median = 0, .stddev = 0, .rms = 0
1971  };
1972  var->stats = var->filtered_stats = var->iqr_stats = stats;
1973  var->stats.center_value = center_value;
1974  }
1975  }
1976  }
1977 
1978  return arguments->val_extract_parser(VALEXTRACT_KEY_VAR, var, arguments->user_input);
1979 }
1980 
1981 static void get_array_ranges(const double *sorted_data, const int length, const double start, const double end, int *start_i, int *end_i) {
1982  *start_i = *end_i = (length >> 1);
1983  int i;
1984  for (i = 0; i < length; i++) {
1985  if (sorted_data[i] >= start) {
1986  *start_i = i;
1987  break;
1988  }
1989  }
1990  for (i = length - 1; i >= 0; i--) {
1991  if (sorted_data[i] <= end) {
1992  *end_i = i;
1993  break;
1994  }
1995  }
1996 }
1997 
1998 static nc_var_stats generate_stats(const double *data, const int offset, const int length) {
1999  int i;
2000  double total = 0;
2001  for (i = offset; i < offset + length; i++) {
2002  total += data[i];
2003  }
2004  double min, max, mean, median;
2005  if (length & 1) {
2006  median = data[offset + (length >> 1)];
2007  } else {
2008  median = (data[offset + (length >> 1) - 1] + data[offset + (length >> 1)]) / 2;
2009  }
2010  min = data[offset];
2011  max = data[offset + length - 1];
2012  mean = total / length;
2013  double stddev = 0, rms = 0;
2014  for (i = offset; i < offset + length; i++) {
2015  stddev += pow(mean - data[i], 2);
2016  rms += pow(data[i], 2);
2017  }
2018  if (length > 1) {
2019 // stddev = sqrt(stddev / length); // population standard deviation
2020  stddev = sqrt(stddev / (length - 1)); // sample standard deviation
2021  rms = sqrt(rms / length);
2022  } else {
2023  stddev = rms = 0;
2024  }
2025 
2026  nc_var_stats stats = {
2027  .initialized = true, .count = length, .center_value = median,
2028  .min = min, .max = max, .mean = mean, .median = median, .stddev = stddev, .rms = rms
2029  };
2030  return stats;
2031 }
2032 
2033 static unsigned nc_get_sizeof(nc_type xtype) {
2034  switch (xtype) {
2035  case NC_BYTE:
2036  case NC_UBYTE:
2037  case NC_CHAR:
2038  return 1;
2039  break;
2040  case NC_SHORT:
2041  case NC_USHORT:
2042  return 2;
2043  break;
2044  case NC_INT: // case NC_LONG:
2045  case NC_UINT:
2046  case NC_FLOAT:
2047  return 4;
2048  break;
2049  case NC_DOUBLE:
2050  case NC_INT64:
2051  case NC_UINT64:
2052  return 8;
2053  break;
2054  case NC_STRING:
2055  return sizeof(char*);
2056  break;
2057  }
2058  return 0;
2059 }
2060 
2061 /* Return the number of digits in the decimal representation of n. */
2062 static unsigned digits(long long n) {
2063  // python -c 'print [pow(10,b) for b in range(0,19)]'
2064  static long long powers[] = {
2065  1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000,
2066  100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000,
2067  100000000000000000, 1000000000000000000
2068  };
2069  // python -c 'print [len(str(pow(2,b))) for b in range(0,128)]'
2070  static unsigned maxdigits[] = {
2071  1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9,
2072  10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16,
2073  16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 22, 23, 23,
2074  23, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 30, 30,
2075  30, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 37, 37,
2076  37, 38, 38, 38, 38, 39
2077  };
2078  unsigned bits = sizeof(n) * CHAR_BIT - __builtin_clz(n);
2079  unsigned digits = maxdigits[bits];
2080  if (n < powers[digits - 1]) {
2081  --digits;
2082  }
2083  return digits;
2084 }
2085 
2086 static char* nc_get_att_text_convert(int ncid, int varid, const char *name) {
2087  int nc_ret;
2088  nc_type xtype;
2089  size_t len = 0;
2090  if ((nc_ret = nc_inq_att(ncid, varid, name, &xtype, &len)) != NC_NOERR) {
2091  return NULL;
2092  }
2093 
2094  char *value;
2095  size_t i;
2096 
2097  switch (xtype) {
2098  case NC_CHAR:
2099  case NC_STRING: //untested
2100 // value = malloc((len+1) * sizeof(char));
2101  value = calloc(len + 1, sizeof(char));
2102  if (value == NULL) {
2103  return NULL;
2104  }
2105  if ((nc_ret = nc_get_att_text(ncid, varid, name, value)) != NC_NOERR) {
2106  return NULL;
2107  }
2108  value[len] = '\0';
2109 
2110  return value;
2111  break;
2112  case NC_BYTE:
2113  case NC_UBYTE:
2114  case NC_SHORT:
2115  case NC_USHORT:
2116  case NC_INT:
2117  case NC_UINT:
2118  case NC_INT64:
2119  case NC_UINT64:
2120  if (1) {
2121  long long *l = malloc(len * sizeof(long long));
2122  if (l == NULL) {
2123  return NULL;
2124  }
2125  if ((nc_ret = nc_get_att_longlong(ncid, varid, name, l)) != NC_NOERR) {
2126  free(l);
2127  return NULL;
2128  }
2129  int calloc_length = len + 1; // spaces, \0, and an extra?
2130  for (i = 0; i < len; i++) {
2131  calloc_length += digits(l[i]);
2132  }
2133  value = calloc(calloc_length, sizeof(char));
2134  if (value == NULL) {
2135  return NULL;
2136  }
2137  for (i = 0; i < len; i++) {
2138  if (i) {
2139  sprintf(value + strlen(value), ", ");
2140  }
2141  sprintf(value + strlen(value), "%lld", l[i]);
2142  }
2143  free(l);
2144  return value;
2145  }
2146  break;
2147  case NC_FLOAT:
2148  case NC_DOUBLE:
2149  if (1) {
2150  double *d = malloc(len * sizeof(double));
2151  if (d == NULL) {
2152  return NULL;
2153  }
2154  if ((nc_ret = nc_get_att_double(ncid, varid, name, d)) != NC_NOERR) {
2155  free(d);
2156  return NULL;
2157  }
2158  int calloc_length = len + 1; // spaces, \0, and an extra?
2159  for (i = 0; i < len; i++) {
2160  calloc_length += digits((long long) d[i]) + 6; // + decimals for %.5f
2161  }
2162  value = calloc(calloc_length, sizeof(char));
2163  if (value == NULL) {
2164  return NULL;
2165  }
2166  for (i = 0; i < len; i++) {
2167  if (i) {
2168  sprintf(value + strlen(value), ", ");
2169  }
2170  sprintf(value + strlen(value), "%.5f", d[i]);
2171  }
2172  free(d);
2173  return value;
2174  }
2175  break;
2176  }
2177  return NULL;
2178 }
2179 
2180 static int process_df_var_attrs(val_extract_arguments *arguments, shash *attributes, int ncid, int varid, bool prepend_path) {
2181  int natts = 0, i, nc_ret;
2182  char attname_full_tmp[prepend_path ? 255 : 1];
2183  char *attname_full = NULL;
2184  size_t att_path_len = 0;
2185  if (prepend_path) {
2186  size_t grpname_len = 0;
2187  if ((nc_ret = nc_inq_grpname_full(ncid, &grpname_len, attname_full_tmp)) != NC_NOERR) {
2188  olog_crit(arguments->log, "Error getting global attributes (1): %d\n", nc_ret);
2189  return VALEXTRACT_ERR_UNKNOWN;
2190  }
2191  attname_full = attname_full_tmp + 1;
2192  att_path_len = strlen(attname_full);
2193  if (att_path_len) {
2194  attname_full[att_path_len++] = '/';
2195  attname_full[att_path_len] = '\0';
2196  }
2197  } else {
2198  attname_full = attname_full_tmp;
2199  attname_full[0] = '\0';
2200  }
2201 
2202  char attname[128], *attvalue;
2203  if ((nc_ret = nc_inq_varnatts(ncid, varid, &natts)) == NC_NOERR) {
2204  for (i = 0; i < natts; i++) {
2205  if ((nc_ret = nc_inq_attname(ncid, varid, i, attname)) != NC_NOERR) {
2206  olog_crit(arguments->log, "Error getting attribute name %s/<%d>: %d\n", attname_full, i, nc_ret);
2207  return VALEXTRACT_ERR_UNKNOWN;
2208  }
2209  if ((attvalue = nc_get_att_text_convert(ncid, varid, attname)) == NULL) {
2210  olog_crit(arguments->log, "Error getting attribute value %s/%s: %d\n", attname_full, attname, nc_ret);
2211  return VALEXTRACT_ERR_UNKNOWN;
2212  }
2213  int shash_set_ret;
2214  if (prepend_path) {
2215  strcpy(attname_full + att_path_len, attname);
2216  shash_set_ret = shash_set(attributes, attname_full, attvalue);
2217  } else {
2218  shash_set_ret = shash_set(attributes, attname, attvalue);
2219  }
2220  if (shash_set_ret) {
2221  free(attvalue);
2222  olog_crit(arguments->log, "Error storing %s\n", attname);
2223  return VALEXTRACT_ERR_UNKNOWN;
2224  }
2225  free(attvalue);
2226  }
2227  } else {
2228  olog_crit(arguments->log, "Error getting varnatts: %d\n", nc_ret);
2229  return VALEXTRACT_ERR_UNKNOWN;
2230  }
2231  return 0;
2232 }
2233 
2234 static int process_df_file_attrs(val_extract_arguments *arguments, nc_region *region) {
2235  nc_file *file = region->file;
2236  int nc_ret, pixel_count = nc_get_region_size(region);
2237  int flags_varid, flags_gid, gid_i;
2238  flags_varid = flags_gid = -1;
2239  for (gid_i = 0; gid_i < file->ngrps; gid_i++) {
2240  if ((nc_ret = nc_inq_varid(file->gids[gid_i], "l2_flags", &flags_varid)) == NC_NOERR) {
2241  flags_gid = file->gids[gid_i];
2242  break;
2243  }
2244  }
2245  if (gid_i == -1 || flags_varid == -1) {
2246  olog_crit(arguments->log, "l2_flags not found, exiting\n");
2247  return VALEXTRACT_ERR_FLAG;
2248  }
2249 
2250  int i, flag_i;
2251  nc_type att_xtype;
2252  size_t u_flag_count;
2253  int flag_count;
2254 
2255  uint32_t *flag_masks;
2256  char **flag_meanings;
2257  int *flag_counts;
2258 
2259  if ((nc_ret = nc_inq_att(flags_gid, flags_varid, "flag_masks", &att_xtype, &u_flag_count)) != NC_NOERR) {
2260  flag_masks = calloc(32, sizeof(uint32_t));
2261  flag_meanings = malloc(32 * sizeof(char*));
2262  flag_counts = calloc(32, sizeof(int));
2263  file->flag_masks = flag_masks;
2264  file->flag_meanings = flag_meanings;
2265  region->flag_counts = flag_counts;
2266 
2267  int flag_string_length = 0;
2268  char att_name[10];
2269  int i = 0;
2270  for (i = 0, u_flag_count = 0; i < 32; i++) {
2271  sprintf(att_name, "f%02zd_name", u_flag_count + 1);
2272  size_t attr_length;
2273  if ((nc_ret = nc_inq_att(flags_gid, flags_varid, att_name, &att_xtype, &attr_length)) != NC_NOERR) {
2274  break;
2275  } else {
2276  u_flag_count++;
2277  flag_string_length += attr_length;
2278  flag_meanings[i] = malloc((attr_length + 1) * sizeof(char));
2279  if ((nc_ret = nc_get_att_text(flags_gid, flags_varid, att_name, flag_meanings[i])) != NC_NOERR) {
2280  olog_crit(arguments->log, "Error getting %s: %d\n", att_name, nc_ret);
2281  return VALEXTRACT_ERR_FLAG;
2282  }
2283  }
2284  }
2285  if (i) {
2286  if (u_flag_count == 33) {
2287  --u_flag_count;
2288  }
2289  flag_count = u_flag_count;
2290  file->flag_string = malloc((flag_string_length + 1) * sizeof(char));
2291  int flag_string_pos = 0;
2292  for (i = 0; i < flag_count; i++) {
2293  flag_masks[i] = 1 << i;
2294  if (i) {
2295  sprintf(&file->flag_string[flag_string_pos++], " ");
2296  }
2297  sprintf(&file->flag_string[flag_string_pos], "%s", flag_meanings[i]);
2298  flag_string_pos += strlen(flag_meanings[i]);
2299  }
2300  } else {
2301  olog_crit(arguments->log, "Error inquiring flag_masks: %d\n", nc_ret);
2302  return VALEXTRACT_ERR_FLAG;
2303  }
2304  } else {
2305  flag_count = u_flag_count;
2306  flag_masks = malloc(u_flag_count * sizeof(uint32_t));
2307  flag_meanings = malloc(u_flag_count * sizeof(char*));
2308  flag_counts = calloc(u_flag_count, sizeof(int));
2309  file->flag_masks = flag_masks;
2310  file->flag_meanings = flag_meanings;
2311  region->flag_counts = flag_counts;
2312  if ((nc_ret = nc_get_att_int(flags_gid, flags_varid, "flag_masks", (int*) flag_masks)) != NC_NOERR) {
2313  olog_crit(arguments->log, "Error getting flag_masks: %d\n", nc_ret);
2314  return VALEXTRACT_ERR_FLAG;
2315  }
2316 
2317  size_t attr_length;
2318  if ((nc_ret = nc_inq_att(flags_gid, flags_varid, "flag_meanings", &att_xtype, &attr_length)) != NC_NOERR) {
2319  olog_crit(arguments->log, "Error inquiring flag_meanings: %d\n", nc_ret);
2320  return VALEXTRACT_ERR_FLAG;
2321  }
2322  char *flag_string = malloc((attr_length + 1) * sizeof(char));
2323  file->flag_string = flag_string;
2324  if ((nc_ret = nc_get_att_text(flags_gid, flags_varid, "flag_meanings", flag_string)) != NC_NOERR) {
2325  olog_crit(arguments->log, "Error getting flag_meanings: %d\n", nc_ret);
2326  return VALEXTRACT_ERR_FLAG;
2327  }
2328  flag_string[attr_length] = '\0';
2329 // olog_debug(arguments->log, "%s\n", flag_string);
2330 
2331  i = 0;
2332  char *token = strtok(flag_string, FLAG_MEANINGS_SEP);
2333  while (token) {
2334  flag_meanings[i++] = token;
2335  token = strtok(NULL, FLAG_MEANINGS_SEP);
2336  }
2337  }
2338 
2339  if (arguments->global_atts) {
2340  if ((file->attributes = shash_create(0)) == NULL) {
2341  olog_crit(arguments->log, "Error creating global attribute hash\n");
2342  return VALEXTRACT_ERR_UNKNOWN;
2343  }
2344  int gid_i;
2345  for (gid_i = 0; gid_i < file->ngrps; gid_i++) {
2346  if (process_df_var_attrs(arguments, file->attributes, file->gids[gid_i], NC_GLOBAL, true)) {
2347  olog_crit(arguments->log, "Error getting group attributes\n");
2348  return VALEXTRACT_ERR_UNKNOWN;
2349  }
2350  }
2351  }
2352 
2353  file->flag_count = flag_count;
2354 
2355  uint32_t *pixel_flags = malloc(sizeof(uint32_t) * pixel_count);
2356  region->pixel_flags = pixel_flags;
2357  if ((nc_ret = nc_get_varr_uint(flags_gid, flags_varid, region, pixel_flags)) != NC_NOERR) {
2358  olog_crit(arguments->log, "Error getting flags data: %d\n", nc_ret);
2359  return VALEXTRACT_ERR_FLAG;
2360  }
2361 
2362 // olog_debug(arguments->log, "Ignore count: %d\n", arguments->ignore_flag_count);
2363  for (i = 0; i < arguments->ignore_flag_count; i++) {
2364 // olog_debug(arguments->log, "Ignore: %s\n", arguments->ignore_flags[i]);
2365  for (flag_i = 0; flag_i < flag_count; flag_i++) {
2366  if (strcmp(flag_meanings[flag_i], arguments->ignore_flags[i]) == 0) {
2367  arguments->ignore_flags_mask |= flag_masks[flag_i];
2368  break;
2369  }
2370  }
2371  if (flag_i == flag_count) {
2372  olog_crit(arguments->log, "Ignore flag %s not found, exiting\n", arguments->ignore_flags[i]);
2373  return VALEXTRACT_ERR_FLAG;
2374  }
2375  }
2376 
2377  for (i = 0; i < pixel_count; i++) {
2378  for (flag_i = 0; flag_i < flag_count; flag_i++) {
2379  if (pixel_flags[i] & flag_masks[flag_i]) {
2380  flag_counts[flag_i]++;
2381  }
2382  }
2383  }
2384 
2385  int unflagged_pixel_count = pixel_count;
2386  uint32_t flag_mask = arguments->ignore_flags_mask;
2387 // olog_debug(arguments->log, "Flag mask: %u\n", arguments->ignore_flags_mask);
2388  for (i = 0; i < pixel_count; i++) {
2389  if (pixel_flags[i] & flag_mask) {
2390  --unflagged_pixel_count;
2391  }
2392  }
2393 
2394 // olog_debug(arguments->log, "Flags to count: %d\n", arguments->count_flag_count);
2395  for (i = 0; i < arguments->count_flag_count; i++) {
2396  olog_debug(arguments->log, "Count: %s\n", arguments->count_flags[i]);
2397  for (flag_i = 0; flag_i < flag_count; flag_i++) {
2398  if (strcmp(flag_meanings[flag_i], arguments->count_flags[i]) == 0) {
2399  arguments->count_flags_mask |= flag_masks[flag_i];
2400  break;
2401  }
2402  }
2403  if (flag_i == flag_count) {
2404  olog_crit(arguments->log, "Ignore flag %s not found, exiting\n", arguments->l2qc_flags[i]);
2405  return VALEXTRACT_ERR_FLAG;
2406  }
2407  }
2408  int flagged_pixel_count = 0;
2409  uint32_t count_flag_mask = arguments->count_flags_mask;
2410 // olog_debug(arguments->log, "Flag count mask: %u\n", arguments->count_flags_mask);
2411  for (i = 0; i < pixel_count; i++) {
2412  if (pixel_flags[i] & count_flag_mask) {
2413  ++flagged_pixel_count;
2414  }
2415  }
2416 
2417 // olog_debug(arguments->log, "L2QC count: %d\n", arguments->l2qc_threshold_count);
2418  int flag_ret = 0;
2419  for (i = 0; i < arguments->l2qc_threshold_count; i++) {
2420  for (flag_i = 0; flag_i < flag_count; flag_i++) {
2421  if (strcmp(flag_meanings[flag_i], arguments->l2qc_flags[i]) == 0) {
2422  if (arguments->l2qc_thresholds[i] >= 0) {
2423  double flag_percent = (double) flag_counts[flag_i] / pixel_count;
2424  if (flag_percent > arguments->l2qc_thresholds[i]) {
2425  flag_ret |= flag_masks[flag_i];
2426  olog_debug(arguments->log, "%s failed (%.3f < %f)\n", flag_meanings[flag_i], arguments->l2qc_thresholds[i], flag_percent);
2427  } else {
2428 // olog_debug(arguments->log, "%s clear (%.3f > %f)\n", flag_meanings[flag_i], arguments->l2qc_thresholds[i], flag_percent);
2429  }
2430  }
2431  break;
2432  }
2433  }
2434  if (flag_i == flag_count) {
2435  olog_crit(arguments->log, "L2QC flag %s not found, exiting\n", arguments->l2qc_flags[i]);
2436  return VALEXTRACT_ERR_FLAG;
2437  }
2438  }
2439 
2440  errno = 0;
2441  int year = process_scan_line_att(arguments, region, "year");
2442  int day = process_scan_line_att(arguments, region, "day");
2443  int msec = process_scan_line_att(arguments, region, "msec");
2444  double lat = process_location_var(arguments, region, "latitude");
2445  double lon = process_location_var(arguments, region, "longitude");
2446 
2447  if (errno) {
2448  olog_crit(arguments->log, "Error processing scan line attributes\n");
2450  }
2451 
2452  {
2453  struct tm t = { .tm_year = year - 1900, .tm_mday = day, .tm_sec = msec / 1000 };
2454  region->utime = mktime(&t) - timezone;
2455  }
2456  {
2457  struct tm t = { .tm_year = year - 1900, .tm_mday = day, .tm_hour = 12 };
2458  mktime(&t);
2459  t.tm_hour = msec / 3600000;
2460  msec -= t.tm_hour * 3600000;
2461  t.tm_min = msec / 60000;
2462  msec -= t.tm_min * 60000;
2463  t.tm_sec = msec / 1000;
2464  msec -= t.tm_sec * 1000;
2465  sprintf(region->ascii_time, "%04d-%02d-%02d %02d:%02d:%02d.%03d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, msec % 1000);
2466  }
2467  region->lat = lat;
2468  region->lon = lon;
2469  region->pixel_count = pixel_count;
2470  region->unflagged_pixel_count = unflagged_pixel_count;
2471  region->flagged_pixel_count = flagged_pixel_count;
2472 
2473  int callback_ret = arguments->val_extract_parser(VALEXTRACT_KEY_FILE, region, arguments->user_input);
2474 
2475  if (callback_ret) {
2476  if (callback_ret != VALEXTRACT_CMD_STOP) {
2477  olog_crit(arguments->log, "val_extract_parser error\n");
2478  return VALEXTRACT_ERR_UNKNOWN;
2479  } else {
2480  return callback_ret;
2481  }
2482  }
2483  if (flag_ret) {
2484  olog_crit(arguments->log, "File failed L2QC check\n");
2485  return VALEXTRACT_ERR_L2QC;
2486  }
2487  return 0;
2488 }
2489 
2490 static double process_scan_line_att(val_extract_arguments *arguments, nc_region *region, const char *name) {
2491  int nc_ret, varid, gid, gid_i;
2492  varid = gid = -1;
2493  for (gid_i = 0; gid_i < region->file->ngrps; gid_i++) {
2494  if ((nc_ret = nc_inq_varid(region->file->gids[gid_i], name, &varid)) == NC_NOERR) {
2495  gid = region->file->gids[gid_i];
2496  break;
2497  }
2498  }
2499  if (gid_i == -1 || varid == -1) {
2500  olog_crit(arguments->log, "scan line attribute %s not found\n", name);
2501  errno |= 1;
2502  return 0;
2503  }
2504 
2505  double line_value = 0;
2506  if ((nc_ret = nc_get_var1_double(gid, varid, &region->center.line, &line_value)) != NC_NOERR) {
2507  olog_crit(arguments->log, "Error getting %s data, %d\n", name, nc_ret);
2508  errno |= 1;
2509  return 0;
2510  }
2511 
2512  return line_value;
2513 }
2514 
2515 static double process_location_var(val_extract_arguments *arguments, nc_region *region, const char *name) {
2516  int nc_ret, varid, gid, gid_i;
2517  varid = gid = -1;
2518  for (gid_i = 0; gid_i < region->file->ngrps; gid_i++) {
2519  if ((nc_ret = nc_inq_varid(region->file->gids[gid_i], name, &varid)) == NC_NOERR) {
2520  gid = region->file->gids[gid_i];
2521  break;
2522  }
2523  }
2524  if (gid_i == -1 || varid == -1) {
2525  olog_crit(arguments->log, "location variable %s not found\n", name);
2526  errno |= 1;
2527  return 0;
2528  }
2529 
2530  double center_value = 0;
2531  if ((nc_ret = nc_get_var1_double(gid, varid, &region->center.line, &center_value)) != NC_NOERR) {
2532  olog_crit(arguments->log, "Error getting %s data, %d\n", name, nc_ret);
2533  errno |= 1;
2534  return 0;
2535  }
2536 
2537  return center_value;
2538 }
2539 
2541  if (arguments->products) {
2542  free(arguments->products);
2543  arguments->products = NULL;
2544  }
2545  if (arguments->ignore_flags) {
2546  int i;
2547  for (i = 0; i < arguments->ignore_flag_count; i++) {
2548  free(arguments->ignore_flags[i]);
2549  }
2550  free(arguments->ignore_flags);
2551  arguments->ignore_flags = NULL;
2552  }
2553  if (arguments->count_flags) {
2554  int i;
2555  for (i = 0; i < arguments->count_flag_count; i++) {
2556  free(arguments->count_flags[i]);
2557  }
2558  free(arguments->count_flags);
2559  arguments->count_flags = NULL;
2560  }
2561  if (arguments->l2qc_flags) {
2562  int i;
2563  for (i = 0; i < arguments->l2qc_threshold_count; i++) {
2564  free(arguments->l2qc_flags[i]);
2565  }
2566  free(arguments->l2qc_flags);
2567  free(arguments->l2qc_thresholds);
2568  arguments->l2qc_flags = NULL;
2569  arguments->l2qc_thresholds = NULL;
2570  }
2571  if (arguments->valid_ranges) {
2572  int i;
2573  for (i = 0; i < arguments->valid_range_count; i++) {
2574  free(arguments->valid_ranges[i].prefix);
2575  }
2576  free(arguments->valid_ranges);
2577  arguments->valid_ranges = NULL;
2578  }
2579  if (arguments->skip_stats && arguments->skip_stats != (char**)skip_stats) {
2580  for (int i = 0; i < arguments->skip_stats_count; i++) {
2581  free(arguments->skip_stats[i]);
2582  }
2583  free(arguments->skip_stats);
2584  arguments->skip_stats = NULL;
2585  }
2586  return 0;
2587 }
2588 
2589 static void print_time() {
2590  time_t epoch_time = time(NULL);
2591  struct tm *current_time = localtime(&epoch_time);
2592  char time_buffer[64];
2593  if (strftime(time_buffer, sizeof(time_buffer), "%A %c", current_time)) {
2594 // olog_debug(arguments->log, "%s\n", time_buffer);
2595  }
2596 }
2597 
2598 // http://stackoverflow.com/a/4771038/1236508 (with reordered and renamed args)
2599 static bool starts_with(const char *str, const char *prefix) {
2600  size_t lenstr = strlen(str), lenpre = strlen(prefix);
2601  return lenstr < lenpre ? false : strncmp(prefix, str, lenpre) == 0;
2602 }
2603 
2604 void unflatten_index(int index, int ndims, const int *dims, int *result) {
2605  int j;
2606  int multiple[ndims];
2607  multiple[ndims - 1] = 1;
2608  for (j = ndims - 2; j >= 0; j--) {
2609  multiple[j] = multiple[j + 1] * dims[j + 1];
2610  }
2611  for (j = 0; j < ndims; j++) {
2612  result[j] = (index / multiple[j]) % dims[j];
2613  }
2614 }
2615 
2616 static void calc_dim_multipliers(int ndims, const int *dims, int *dim_multipliers) {
2617  int j;
2618  dim_multipliers[ndims - 1] = 1;
2619  for (j = ndims - 2; j >= 0; j--) {
2620  dim_multipliers[j] = dim_multipliers[j + 1] * dims[j + 1];
2621  }
2622 }
2623 
2624 static int flatten_index(int ndims, const int *dim_multipliers, int *index) {
2625  int ret = 0, j;
2626  for (j = ndims - 1; j >= 0; j--) {
2627  ret += (index[j] * dim_multipliers[j]);
2628  }
2629  return ret;
2630 }
2631 
2632 const char* val_extract_version() {
2634 }
2637 }
2638 
2639 // The following should go into a math library
2640 
2641 #define define_compare(type, suffix) \
2642 int compare ## suffix(const void *a, const void *b){ \
2643  if (*(const type*)a < *(const type*)b){ \
2644  return -1; \
2645  } else if (*(const type*)a > *(const type*)b){ \
2646  return 1; \
2647  } else { \
2648  return 0; \
2649  } \
2650 }
2651 
2652 define_compare(int8_t, _int8)
2653 define_compare(uint8_t, _uint8)
2654 define_compare(int16_t, _int16)
2655 define_compare(uint16_t, _uint16)
2656 define_compare(int32_t, _int32)
2657 define_compare(uint32_t, _uint32)
2658 define_compare(int64_t, _int64)
2659 define_compare(uint64_t, _uint64)
2660 define_compare(float, _float)
2661 define_compare(double, _double)
2662 
2663 static double strtod_strict(const char *str) {
2664  char *endptr;
2665  errno = 0;
2666  double ret = strtod(str, &endptr);
2667  if (!errno && *endptr != '\0') {
2668  errno = 1;
2669  }
2670  return ret;
2671 }
2672 
2673 // The following should go into an nc_read library
2674 
2676  int i;
2677  size_t ret = 0;
2678  for (i = 0; i < region->box_count; i++) {
2679  ret += region->boxes[i].count[0] * region->boxes[i].count[1];
2680  }
2681  return ret;
2682 }
2683 size_t nc_get_region_dim_size(nc_region *region, int dim_index) {
2684  int i;
2685  size_t ret = 0;
2686  for (i = 0; i < region->box_count; i++) {
2687  ret += region->boxes[i].count[dim_index];
2688  }
2689  return ret;
2690 }
2691 
2692 #define define_nc_get_varr(type, suffix) \
2693 int nc_get_varr ## suffix(int ncid, int varid, nc_region *region, type *p){ \
2694  int i, nc_ret = 0; \
2695  for (i=0;i<region->box_count;i++){ \
2696  if ((nc_ret = nc_get_vara ## suffix(ncid, varid, region->boxes[i].start, region->boxes[i].count, p)) != NC_NOERR){ \
2697  break; \
2698  } \
2699  p += region->boxes[i].count[0] * region->boxes[i].count[1]; \
2700  } \
2701  return nc_ret; \
2702 } \
2703 int nc_get_varrd ## suffix(int ncid, int varid, nc_region *region, int dim_index, type *p){ \
2704  int i, nc_ret = 0; \
2705  for (i=0;i<region->box_count;i++){ \
2706  if ((nc_ret = nc_get_vara ## suffix(ncid, varid, region->boxes[i].start + dim_index, region->boxes[i].count + dim_index, p)) != NC_NOERR){ \
2707  break; \
2708  } \
2709  p += region->boxes[i].count[dim_index]; \
2710  } \
2711  return nc_ret; \
2712 }
2713 
2714 define_nc_get_varr(char, _text)
2715 define_nc_get_varr(unsigned char, _uchar)
2716 define_nc_get_varr(signed char, _schar)
2717 define_nc_get_varr(short, _short)
2718 define_nc_get_varr(int, _int)
2719 define_nc_get_varr(long, _long)
2720 define_nc_get_varr(float, _float)
2721 define_nc_get_varr(double, _double)
2722 define_nc_get_varr(unsigned short, _ushort)
2723 define_nc_get_varr(unsigned int, _uint)
2724 define_nc_get_varr(long long, _longlong)
2725 define_nc_get_varr(unsigned long long, _ulonglong)
2726 define_nc_get_varr(char *, _string)
2727 define_nc_get_varr(void,)
double median
Definition: val_extract.h:269
void * data
Definition: val_extract.h:288
#define VAR_ATT_KEY
Definition: val_extract.c:62
an array had not been initialized Several spelling and grammar corrections were which is read from the appropriate MCF the above metadata values were hard coded A problem calculating the average background DN for SWIR bands when the moon is in the space view port was corrected The new algorithm used to calculate the average background DN for all reflective bands when the moon is in the space view port is now the same as the algorithm employed by the thermal bands For non SWIR changes in the averages are typically less than Also for non SWIR the black body DNs remain a backup in case the SV DNs are not available For SWIR the changes in computed averages were larger because the old which used the black body suffered from contamination by the micron leak As a consequence of the if SV DNs are not available for the SWIR the EV pixels will not be the granule time is used to identify the appropriate tables within the set given for one LUT the first two or last two tables respectively will be used for the interpolation If there is only one LUT in the set of it will be treated as a constant LUT The manner in which Earth View data is checked for saturation was changed Previously the raw Earth View DNs and Space View DNs were checked against the lookup table values contained in the table dn_sat The change made is to check the raw Earth and Space View DNs to be sure they are less than the maximum saturation value and to check the Space View subtracted Earth View dns against a set of values contained in the new lookup table dn_sat_ev The metadata configuration and ASSOCIATEDINSTRUMENTSHORTNAME from the MOD02HKM product The same metatdata with extensions and were removed from the MOD021KM and MOD02OBC products ASSOCIATEDSENSORSHORTNAME was set to MODIS in all products These changes are reflected in new File Specification which users may consult for exact the pow functions were eliminated in Emissive_Cal and Emissive bands replaced by more efficient code Other calculations throughout the code were also made more efficient Aside from a few round off there was no difference to the product The CPU time decreased by about for a day case and for a night case A minor bug in calculating the uncertainty index for emissive bands was corrected The frame index(0-based) was previously being used the frame number(1-based) should have been used. There were only a few minor changes to the uncertainty index(maximum of 1 digit). 3. Some inefficient arrays(Sigma_RVS_norm_sq) were eliminated and some code lines in Preprocess_L1A_Data were moved into Process_OBCEng_Emiss. There were no changes to the product. Required RAM was reduced by 20 MB. Now
int32 value
Definition: Granule.c:1235
bool uses_control_points
Definition: val_extract.h:278
int olog_crit(olog *olog,...)
Master structure containing options, document strings, child parsers, and text filters....
Definition: argpar.h:398
data_t t[NROOTS+1]
Definition: decode_rs.h:77
#define VALEXTRACT_ERR_FLAG
Returned when the something goes wrong processing l2_flags.
Definition: val_extract.h:37
int j
Definition: decode_rs.h:73
int32_t day
#define END_LINE_KEY
Definition: val_extract.c:45
#define clean_and_return(ret)
Definition: val_extract.c:547
void pqueue_push(pqueue *pq, double priority, void *item)
Definition: pqueue-ll.c:34
int * flag_counts
Definition: val_extract.h:259
time_t utime
Definition: val_extract.h:262
#define VALEXTRACT_ERR_POINT_NOT_FOUND
Returned when the desired point is not in the file boundaries.
Definition: val_extract.h:28
float mean(float *xs, int sample_size)
Definition: numerical.c:81
These are used to scale the SD before writing it to the HDF4 file The default is and which means the product is not scaled at all Since the product is usually stored as a float inside of this is a way to write the float out as a integer l2prod min
nc_box * boxes
Definition: val_extract.h:257
#define START_LINE_KEY
Definition: val_extract.c:44
#define OPTION_DOC_NO_BREAK
Do not add an extra newline after this documentation string. Useful for lists and manual formatting.
Definition: argpar.h:171
bool has_offset
Definition: val_extract.h:279
int pixel_count
Definition: val_extract.h:258
int gid
Definition: val_extract.h:277
nc_point center
Definition: val_extract.h:255
#define str(s)
Definition: val_extract.c:32
#define VALEXTRACT_ERR_NONE
Returned on successful processing.
Definition: val_extract.h:26
#define NULL
Definition: decode_rs.h:63
int valid_data_count
Definition: val_extract.h:287
#define OPTION_INT
Cast this option as a long. The value and any error will be reflected in the argpar_state struct duri...
Definition: argpar.h:160
int neighbor_count
Definition: val_extract.c:1083
int neighbors[8]
Definition: val_extract.c:1083
const double deg2rad
Provides a single function to calculate geographical distances.
#define ARGPAR_ERR_USAGE
Returned from the parser callback to signal argpar to stop parsing, print a usage summary,...
Definition: argpar.h:338
void pqueue_destroy(pqueue *pq)
Definition: pqueue-ll.c:24
Passed into the library function to control the processing. Many of the fields will be unspecified.
Definition: val_extract.h:103
double vincenty_distance(double lat1, double lon1, double lat2, double lon2)
Calculate geographical distances using Vincenty's algorithm.
Definition: vincenty.c:47
const double pi
#define ARGPAR_KEY_SUCCESS
Passed as the key to each parser callback function when parsing has finished and no errors were retur...
Definition: argpar.h:295
bool initialized
Definition: val_extract.h:267
integer *4 function lenstr(string)
Definition: lenstr.f:2
bool has_scale
Definition: val_extract.h:279
float * lat
int32 * msec
Definition: l1_czcs_hdf.c:31
float tm[MODELMAX]
Given to val_extract_arguments, these contain the valid ranges, inclusive, of variables....
Definition: val_extract.h:87
int flagged_pixel_count
Definition: val_extract.h:258
shash * shash_create(uint32_t options)
Initialize a shash object.
Definition: shash.c:105
const char * val_extract_api_version()
Returns a string representation of the library's implemented API version number, without a label.
Definition: val_extract.c:2635
int shash_set(shash *h, const char *key, const char *value)
Add or overwrite a pointer, associating it with the given key.
Definition: shash.c:224
Implementation-specific, generic type to store the shash object.
Definition: shash.c:40
#define ELON_KEY
Definition: val_extract.c:54
#define ARGPAR_KEY_INIT
Passed as the key to each parser callback function before any parsing occurs. For most cases,...
Definition: argpar.h:287
bool is_geospatial
Definition: val_extract.h:278
const char * val_extract_version()
Returns a string representation of the library's version number, without a label.
Definition: val_extract.c:2632
int ngrps
Definition: val_extract.h:244
#define VALEXTRACT_KEY_INIT
Passed to parser when beginning the processing.
Definition: val_extract.h:50
A simple logger, capable of dispatching log events to multiple end points.
size_t data_sizeof
Definition: val_extract.h:285
int pixel_control_dimid
Definition: val_extract.h:245
struct packet_stats stats
#define L2QC_KEY
Definition: val_extract.c:56
int ndims
Definition: val_extract.h:281
size_t pixel
Definition: val_extract.h:234
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude resolving resolving GSFcd00179 Corrected handling of fill values for[Sensor|Solar][Zenith|Azimuth] resolving MODxl01751 Changed to validate LUT version against a value retrieved from the resolving MODxl02056 Changed to calculate Solar Diffuser angles without adjustment for estimated post launch changes in the MODIS orientation relative to incidentally resolving defects MODxl01766 Also resolves MODxl01947 Changed to ignore fill values in SCI_ABNORM and SCI_STATE rather than treating them as resolving MODxl01780 Changed to use spacecraft ancillary data to recognise when the mirror encoder data is being set by side A or side B and to change calculations accordingly This removes the need for seperate LUTs for Side A and Side B data it makes the new LUTs incompatible with older versions of the and vice versa Also resolves MODxl01685 A more robust GRing algorithm is being which will create a non default GRing anytime there s even a single geolocated pixel in a granule Removed obsolete messages from seed file
Definition: HISTORY.txt:413
double center_value
Definition: val_extract.h:270
unsigned j
Definition: val_extract.c:1084
nc_region * region
Definition: val_extract.h:275
#define ELAT_KEY
Definition: val_extract.c:52
unsigned i
Definition: val_extract.c:1084
#define START_PIXEL_KEY
Definition: val_extract.c:46
#define END_PIXEL_KEY
Definition: val_extract.c:47
char * name
Definition: val_extract.h:276
nc_var_stats iqr_stats
Definition: val_extract.h:283
a context in which it is NOT documented to do so subscript error
Definition: HISTORY.txt:53
int olog_debug(olog *olog,...)
int state(double tjdTDB, JPLIntUtilType *util, double posvel[13][6], double *pnut)
#define ARG_DBL_START
Definition: val_extract.c:68
void pqueue_repush(pqueue *pq, double priority, void *item)
Definition: pqueue-ll.c:54
#define COUNT_KEY
Definition: val_extract.c:60
shash * attributes
Definition: val_extract.h:282
data_t tmp
Definition: decode_rs.h:74
double stddev
Definition: val_extract.h:269
#define IGNORE_KEY
Definition: val_extract.c:58
#define VALEXTRACT_ERR_INPUT
Returned when given bad or no arguments.
Definition: val_extract.h:42
#define BOX_SIZE_KEY
Definition: val_extract.c:48
bool has_fill
Definition: val_extract.h:279
nc_var_stats filtered_stats
Definition: val_extract.h:283
MOD_PR02AQUA Production where MYD is the prefix denoting AQUA file output The total
#define VALEXTRACT_API_VERSION_STR
Definition: val_extract.c:5
#define VALEXTRACT_ERR_VARIABLE
Returned when the something goes wrong processing a product or when finding a product specified on th...
Definition: val_extract.h:40
olog * olog_load_default()
Definition: olog_loader.c:12
#define OPTION_HIDDEN
Do not display this option anywhere in the usage summary.
Definition: argpar.h:149
#define VALEXTRACT_KEY_VAR
Passed to parser when a variable is processed. The parser is passed a pointer to an nc_var structure ...
Definition: val_extract.h:56
#define ARGPAR_ERR_ABORT
Returned from the parser callback to signal argpar to stop parsing and return to the caller.
Definition: argpar.h:334
int varid
Definition: val_extract.h:277
size_t nc_get_region_size(nc_region *region)
Definition: val_extract.c:2675
#define define_nc_get_varr(type, suffix)
Definition: val_extract.c:2692
const char * ifile
Definition: argpar.c:44
int closest_index(vector< float > const &refvals, const float value)
Definition: get_mld.cpp:198
int line_dimid
Definition: val_extract.h:245
int errno
double core
Definition: val_extract.c:1082
subroutine diff(x, conec, n, dconecno, dn, dconecmk, units, u, inno, i, outno, o, input, deriv)
Definition: ffnet.f:205
integer, parameter double
#define VALEXTRACT_CMD_STOP
Returned from a parser to stop processing.
Definition: val_extract.h:65
this program makes no use of any feature of the SDP Toolkit that could generate such a then geolocation is calculated at that and then aggregated up to Resolved feature request Bug by adding three new int8 SDSs for each high resolution pixel
Definition: HISTORY.txt:192
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude data
Definition: HISTORY.txt:356
#define isspace(c)
#define define_compare(type, suffix)
Definition: val_extract.c:2641
#define SLAT_KEY
Definition: val_extract.c:51
double offset
Definition: val_extract.h:280
double y
Definition: val_extract.c:1080
#define BOX_SIZE_KM_KEY
Definition: val_extract.c:50
argpar val_extract_argpar
argpar structure used for making programs that inherit options from this library.
Definition: val_extract.c:523
#define RANGES_KEY
Definition: val_extract.c:57
int pixel_dimid
Definition: val_extract.h:245
double lon
Definition: val_extract.h:261
#define OPTION_DOC
This option isn't actually an option, merely text for the usage summary.
Definition: argpar.h:152
double x
Definition: val_extract.c:1080
#define VALEXTRACT_ERR_L2QC
Not a real error, but returned when the L2QC step says it's a poor quality file.
Definition: val_extract.h:44
nc_var_stats stats
Definition: val_extract.h:283
#define GLOBAL_ATT_KEY
Definition: val_extract.c:61
State variable to be filled before each call to the parser callback.
Definition: argpar.h:196
#define VALEXTRACT_IMPLEMENTATION_STR
Definition: val_extract.c:3
this program makes no use of any feature of the SDP Toolkit that could generate such a then geolocation is calculated at that and then aggregated up to Resolved feature request Bug by adding three new int8 SDSs for each high resolution offsets between the high resolution geolocation and a bi linear interpolation extrapolation of the positions This can be used to reconstruct the high resolution geolocation Resolved Bug by delaying cumulation of gflags until after validation of derived products Resolved Bug by setting Latitude and Longitude to the correct fill resolving to support Near Real Time because they may be unnecessary if use of entrained ephemeris and attitude data is turned resolving bug report Corrected to filter out Aqua attitude records with missing status helping resolve bug MOD_PR03 will still correctly write scan and pixel data that does not depend upon the start time
Definition: HISTORY.txt:248
A simple dictionary library for storing strings.
size_t start[2]
Definition: val_extract.h:238
#define fabs(a)
Definition: misc.h:93
double reachability
Definition: val_extract.c:1082
#define VALEXTRACT_UNSET
Initial value for location arguments (lat/lon/line/pixl) before argument parsing.
Definition: val_extract.h:68
int box_count
Definition: val_extract.h:256
nc_type data_type
Definition: val_extract.h:284
size_t nc_get_region_dim_size(nc_region *region, int dim_index)
Definition: val_extract.c:2683
PARAM_TYPE_NONE Default value No parameter is buried in the product name name_prefix is case insensitive string compared to the product name PARAM_TYPE_VIS_WAVE The visible wavelength bands from the sensor are buried in the product name The product name is compared by appending and name_suffix ie aph_412_giop where prod_ix will be set to PARAM_TYPE_IR_WAVE same search method as PARAM_TYPE_VIS_WAVE except only wavelength above are looped through but prod_ix is still based ie aph_2_giop for the second and prod_ix set to PARAM_TYPE_INT name_prefix is compared with the beginning of the product name If name_suffix is not empty the it must match the end of the product name The characters right after the prefix are read as an integer and prod_ix is set to that number strncpy(l2prod->name_prefix, "myprod", UNITLEN)
#define VALEXTRACT_ERR_NCFILE_INVALID
Returned when the NetCDF file isn't in the format expected (not an L2, etc).
Definition: val_extract.h:35
Library for reading command-line arguments in the form of key=value.
#define OPTICS_KEY
Definition: val_extract.c:59
float * lon
#define VALEXTRACT_OPTICS_DEFAULT
Definition: val_extract.c:39
bool processed
Definition: val_extract.c:1081
#define ARG_INT_START
Definition: val_extract.c:65
int * dim_ids
Definition: val_extract.h:281
#define SLON_KEY
Definition: val_extract.c:53
pqueue * pqueue_create()
Definition: pqueue-ll.c:18
void * pqueue_pull(pqueue *pq)
Definition: pqueue-ll.c:94
Process a small section of a Level-2 NetCDF file.
#define DBL_MAX
Definition: make_L3_v1.1.c:60
int val_extract(val_extract_arguments *arguments)
Process a small section of a Level-2 NetCDF file.
Definition: val_extract.c:589
size_t count[2]
Definition: val_extract.h:238
void unflatten_index(int index, int ndims, const int *dims, int *result)
Given dimension lengths from an nc_file and a one dimensional index, find the corresponding n-dimensi...
Definition: val_extract.c:2604
#define SKIP_STATS_KEY
Definition: val_extract.c:63
l2prod offset
char * group_name
Definition: val_extract.h:276
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude resolving resolving GSFcd00179 Corrected handling of fill values for[Sensor|Solar][Zenith|Azimuth] resolving MODxl01751 Changed to validate LUT version against a value retrieved from the resolving MODxl02056 Changed to calculate Solar Diffuser angles without adjustment for estimated post launch changes in the MODIS orientation relative to incidentally resolving defects MODxl01766 Also resolves MODxl01947 Changed to ignore fill values in SCI_ABNORM and SCI_STATE rather than treating them as resolving MODxl01780 Changed to use spacecraft ancillary data to recognise when the mirror encoder data is being set by side A or side B and to change calculations accordingly This removes the need for seperate LUTs for Side A and Side B data it makes the new LUTs incompatible with older versions of the and vice versa Also resolves MODxl01685 A more robust GRing algorithm is being which will create a non default GRing anytime there s even a single geolocated pixel in a granule Removed obsolete messages from seed as required for compatibility with version of the SDP toolkit Corrected test output file names to end in per delivery and then split off a new MYD_PR03 pcf file for Aqua Added AssociatedPlatformInstrumentSensor to the inventory metadata in MOD01 mcf and MOD03 mcf Created new versions named MYD01 mcf and MYD03 where AssociatedPlatformShortName is rather than Terra The program itself has been changed to read the Satellite Instrument validate it against the input L1A and LUT and to use it determine the correct files to retrieve the ephemeris and attitude data from Changed to produce a LocalGranuleID starting with MYD03 if run on Aqua data Added the Scan Type file attribute to the Geolocation copied from the L1A and attitude_angels to radians rather than degrees The accumulation of Cumulated gflags was moved from GEO_validate_earth_location c to GEO_locate_one_scan c
Definition: HISTORY.txt:464
int shash_destroy(shash *h)
Destroy a shash object, free'ing the memory used.
Definition: shash.c:136
double fill
Definition: val_extract.h:280
char ascii_time[24]
Definition: val_extract.h:263
const double rad2deg
int unflagged_pixel_count
Definition: val_extract.h:258
#define VALEXTRACT_ERR_NCFILE_ERR
Returned when the NetCDF file can't be opened (due to errors or corruption).
Definition: val_extract.h:33
int val_extract_clean(val_extract_arguments *arguments)
Clean up stuff malloc'd by the argpar callback. Should be called at the end of processing if val_extr...
Definition: val_extract.c:2540
double lat
Definition: val_extract.h:261
long timezone
int i
Definition: decode_rs.h:71
double scale
Definition: val_extract.h:280
nc_file * file
Definition: val_extract.h:254
#define VALEXTRACT_ERR_UNKNOWN
Returned for unexpected errors like malloc failures or, possibly, permissions problems and the like.
Definition: val_extract.h:31
#define VALEXTRACT_KEY_FILE
Passed to parser when first opening the NetCDF file. The parser is passed a pointer to an nc_region s...
Definition: val_extract.h:53
void val_extract_clear_args(val_extract_arguments *arguments)
Clear an argument struct to make it suitable for using the library without using argpar.
Definition: val_extract.c:127
How many dimensions is the output array Default is Not sure if anything above will work correctly strcpy(l2prod->title, "no title yet")
int olog_notice(olog *olog,...)
Stores the configuration for a par argument.
Definition: argpar.h:87
#define OPTION_DBL
Cast this option as a double. The value and any error will be reflected in the argpar_state struct du...
Definition: argpar.h:156
float p[MODELMAX]
Definition: atrem_corl1.h:131
size_t line
Definition: val_extract.h:234
nc_file * file
Definition: val_extract.h:274
l2prod max
double * valid_data
Definition: val_extract.h:286
a context in which it is NOT documented to do so subscript which cannot be easily calculated when extracting TONS attitude data from the Terra L0 files Corrected several defects in extraction of entrained ephemeris and and as HDF file attributes
Definition: HISTORY.txt:65
uint32_t * pixel_flags
Definition: val_extract.h:260
int * gids
Definition: val_extract.h:244