NASA Logo
Ocean Color Science Software

ocssw V2022
get_habs.c
Go to the documentation of this file.
1 /*
2  * get_hab.c
3  *
4  * Harmful Algal Blooms
5  * Created on: Aug 31, 2015
6  * Author: Rick Healy (richard.healy@nasa.gov)
7  */
8 #include <stdlib.h>
9 #include <math.h>
10 #include "l12_proto.h"
11 #include <sensorInfo.h>
12 #include "mph_flags.h"
13 /*
14  * Harmful Algal Bloom Indexes and related functions
15  * for flagging products and cloud masking
16  * R. Healy 9/1/2015 (richard.healy@nasa.gov)
17  *
18  * Cyanobacteria Index
19  * Wynne,T.T.; Stumpf, R.P.; 2013. Spatial and Temporal Patterns in the Seasonal Distribution of
20  Toxic Cyanobacteria in Western Lake Erie from 2002–2014,Toxins 2015, 7, 1649-1663; doi:10.3390/toxins7051649
21 
22  Maximum Chlorophyll Index
23  C.E. Binding ⁎, T.A. Greenberg, R.P. Bukata, The MERIS Maximum Chlorophyll Index; its merits and limitations for inland water
24  algal bloom monitoring, JGLR-00579
25  *
26  */
27 
28 static int numFlagMPHPixels = -1;
29 static int numFlagHABSPixels = -1;
30 static uint8_t *flags_mph = NULL;
31 static uint8_t *flags_habs = NULL;
32 static int32_t mask = LAND;
33 
34 void allocateFlagsMPH(int numPix) {
35  if((numFlagMPHPixels != numPix) || !flags_mph) {
36  numFlagMPHPixels = numPix;
37  if(flags_mph)
38  free(flags_mph);
39  flags_mph = (uint8_t*) malloc(numPix);
40  }
41 }
42 void allocateFlagsHABS(int numPix) {
43  if((numFlagHABSPixels != numPix) || !flags_habs) {
44  numFlagHABSPixels = numPix;
45  if(flags_habs)
46  free(flags_habs);
47  flags_habs = (uint8_t*) malloc(numPix);
48  }
49 }
50 
51 void habs_meris_ci_corr(float rhos443, float rhos490, float rhos560,
52  float rhos620, float rhos665, float rhos709, float rhos865,
53  float rhos885, float *ci) {
54 
55  float kd, kd_709, ss_560;
56 
57  kd = (0.7 * ((((rhos620 + rhos665) / 2.) - rhos885) / (((rhos443 + rhos490) / 2.) - rhos885)));
58 
59  //calculate Kd using rhos_709 instead of rhos_620 & rhos_665
60  kd_709 = (0.7 * ((rhos709 - rhos885) / (((rhos443 + rhos490) / 2.) - rhos885)));
61 
62  //709 switching modification for scum conditions
63  if (kd_709 > kd) kd = kd_709;
64 
65  //expect a 560 peak with cyano blooms
66  ss_560 = rhos560 - rhos443 + (rhos443 - rhos620)*(560 - 443) / (620 - 443);
67 
68  //identify over-corrected pixels that would result in negative Kd's
69  if (((((rhos620 + rhos665) / 2.) - rhos885) > 0) &&
70  ((((rhos443 + rhos490) / 2.) - rhos885) > 0)) {
71  if ((kd < 0.25) &&
72  ((rhos865 <= rhos490) ||
73  (rhos865 <= rhos665) ||
74  (rhos865 <= rhos709)) &&
75  (ss_560 < 0.01)) {
76  *ci = 0;
77  }
78  } else {
79  if (rhos885 < 0.005) {
80  *ci = 0;
81  }
82  }
83 }
84 
85 void get_habs_ci(l2str *l2rec, l2prodstr *p, float ci[]) {
86 
87  int ib0, ib1, ib2, ib3, ib4, ib5, ib6, ib7, ib8;
88  float wav0, wav1, wav2, wav3, wav4, wav5, wav6, wav7, wav8, fac, w681_fac;
89  int nonci;
90 
91  int ip, ipb;
92  static productInfo_t* ci_product_info;
93 
94  switch (l2rec->l1rec->l1file->sensorID) {
95  case MODISA:
96  case MODIST:
97  fac = 1.3;
98  w681_fac = 1.0;
99  break;
100  case OLCIS3A:
101  case OLCIS3B:
102  fac = 1.052;
103  w681_fac = 0.99;
104  break;
105  default:
106  fac = 1.0;
107  w681_fac = 1.0;
108  }
109  switch (p->cat_ix) {
110 
111  case CAT_CI_stumpf:
112  case CAT_CI_cyano:
113  case CAT_CI_noncyano:
114  // Cyanobacteria Index
115  // Wynne, Stumpf algorithm 2013
116  switch (l2rec->l1rec->l1file->sensorID) {
117  case MODISA:
118  case MODIST:
119  wav0 = 547;
120  wav1 = 667;
121  wav2 = 678;
122  wav3 = 748;
123  break;
124  default:
125  wav0 = 620;
126  wav1 = 665;
127  wav2 = 681;
128  wav3 = 709;
129  wav4 = 443;
130  wav5 = 490;
131  wav6 = 560;
132  wav7 = 865;
133  wav8 = 885;
134  ib4 = bindex_get(wav4);
135  ib5 = bindex_get(wav5);
136  ib6 = bindex_get(wav6);
137  ib7 = bindex_get(wav7);
138  ib8 = bindex_get(wav8);
139  }
140  ib0 = bindex_get(wav0);
141  break;
142 
143  case CAT_MCI_stumpf:
144  if (l2rec->l1rec->l1file->sensorID != MERIS &&
145  l2rec->l1rec->l1file->sensorID != OLCIS3A &&
146  l2rec->l1rec->l1file->sensorID != OLCIS3B) {
147  printf("MCI not supported for this sensor (%s).\n",
148  sensorId2SensorName(l2rec->l1rec->l1file->sensorID));
149  exit(EXIT_FAILURE);
150  }
151  wav1 = 681;
152  wav2 = 709;
153  wav3 = 754;
154  break;
155 
156  default:
157  printf("HABS_CI: Hmm, something's really messed up.\n");
158  exit(EXIT_FAILURE);
159  }
160 
161  if (ci_product_info == NULL) {
162  ci_product_info = allocateProductInfo();
163  findProductInfo("CI_cyano", l2rec->l1rec->l1file->sensorID, ci_product_info);
164  }
165 
166  ib1 = bindex_get(wav1);
167  ib2 = bindex_get(wav2);
168  ib3 = bindex_get(wav3);
169 
170  if (ib1 < 0 || ib2 < 0 || ib3 < 0) {
171  printf("(M)CI_stumpf: incompatible sensor wavelengths for this algorithm\n");
172  exit(EXIT_FAILURE);
173  }
174 
175  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
176 
177  ipb = l2rec->l1rec->l1file->nbands*ip;
178  // check for near coastal and inland waters (height == 0 and depth < shallow_water_depth)
179  // and valid data for processing CI
180  if ((l2rec->l1rec->height[ip] == 0 && l2rec->l1rec->dem[ip] < -1 * input->shallow_water_depth) ||
181  (l2rec->l1rec->flags[ip] & mask) != 0 ||
182  l2rec->l1rec->rhos[ipb + ib1] <= 0.0 ||
183  l2rec->l1rec->rhos[ipb + ib2] <= 0.0 ||
184  l2rec->l1rec->rhos[ipb + ib3] <= 0.0) {
185  ci[ip] = BAD_FLT;
186  l2rec->l1rec->flags[ip] |= PRODFAIL;
187  } else {
188  switch (p->cat_ix) {
189 
190  case CAT_CI_stumpf:
191  case CAT_CI_cyano:
192  case CAT_CI_noncyano:
193  ci[ip] = fac * ((l2rec->l1rec->rhos[ipb + ib3] - l2rec->l1rec->rhos[ipb + ib1])*(wav2 - wav1) / (wav3 - wav1)
194  - (l2rec->l1rec->rhos[ipb + ib2]*w681_fac - l2rec->l1rec->rhos[ipb + ib1]));
195 
196  //following corrections currently only applicable for MERIS/OLCI
197  if (l2rec->l1rec->l1file->sensorID == MERIS || l2rec->l1rec->l1file->sensorID == OLCIS3A
198  || l2rec->l1rec->l1file->sensorID == OLCIS3B) {
199 
200  //turbidity correction based on ss620
201  if (l2rec->l1rec->rhos[ipb + ib0] - l2rec->l1rec->rhos[ipb + ib6]
202  + (l2rec->l1rec->rhos[ipb + ib6] - l2rec->l1rec->rhos[ipb + ib1]) * (wav0 - wav6) / (wav1 - wav6) > 0) {
203  ci[ip] = 0;
204  }
205 
206  habs_meris_ci_corr(l2rec->l1rec->rhos[ipb + ib4], l2rec->l1rec->rhos[ipb + ib5],
207  l2rec->l1rec->rhos[ipb + ib6], l2rec->l1rec->rhos[ipb + ib0],
208  l2rec->l1rec->rhos[ipb + ib1], l2rec->l1rec->rhos[ipb + ib3],
209  l2rec->l1rec->rhos[ipb + ib7], l2rec->l1rec->rhos[ipb + ib8], &ci[ip]);
210 
211  if (p->cat_ix == CAT_CI_cyano || p->cat_ix == CAT_CI_noncyano) {
212  if (l2rec->l1rec->rhos[ipb + ib1]
213  - l2rec->l1rec->rhos[ipb + ib0]
214  + (l2rec->l1rec->rhos[ipb + ib0]
215  - l2rec->l1rec->rhos[ipb + ib2]*w681_fac)
216  *(wav1 - wav0) / (wav2 - wav0) >= 0) {
217  nonci = 0;
218  } else {
219  nonci = 1;
220  }
221 
222  if (l2rec->l1rec->rhos[ipb + ib0] <= 0 && p->cat_ix == CAT_CI_noncyano) {
223  ci[ip] = BAD_FLT;
224  l2rec->l1rec->flags[ip] |= PRODFAIL;
225  } else {
226  if (p->cat_ix == CAT_CI_noncyano) {
227 
228  if (nonci == 0) ci[ip] = 0;
229  } else {
230  if (nonci == 1) ci[ip] = 0;
231  }
232  }
233  }
234  }
235  break;
236  case CAT_MCI_stumpf:
237  ci[ip] = fac * (l2rec->l1rec->rhos[ipb + ib2] - l2rec->l1rec->rhos[ipb + ib1]*w681_fac
238  - (l2rec->l1rec->rhos[ipb + ib3] - l2rec->l1rec->rhos[ipb + ib1]*w681_fac)*(wav2 - wav1) / (wav3 - wav1));
239  break;
240  default:
241  ci[ip] = BAD_FLT;
242  break;
243  }
244 
245  // set non-detect levels of CI to the minimum valid value in product.xml definition
246  if (ci[ip] < ci_product_info->validMin) {
247  ci[ip] = ci_product_info->validMin;
248  }
249  }
250  }
251 
252  flags_habs = get_flags_habs(l2rec);
253  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
254  if (flags_habs[ip] != 0){
255  ci[ip] = BAD_FLT;
256  l2rec->l1rec->flags[ip] |= PRODFAIL;
257  }
258  }
259  if (l2rec->l1rec->iscan == l2rec->l1rec->l1file->nscan) {
260  freeProductInfo(ci_product_info);
261  }
262 }
263 /*
264  * Maximum Peak Height of chlorophyll for MERIS
265  *
266  * Updated: J. Scott (6/1/2018) joel.scott@nasa.gov; removed Rrs check, fixed mistyped coefficient, removed negative chl filter
267  *
268  * R. Healy (9/1/2015) richard.healy@nasa.gov
269  *
270  * Mark William Matthews , Daniel Odermatt
271  * Remote Sensing of Environment 156 (2015) 374–382
272  *
273  *
274  */
275 void get_habs_mph(l2str *l2rec, l2prodstr *p, float chl_mph[]) {
276  float wav6, wav7, wav8, wav9, wav10, wav14;
277  int ip, ipb, ib6, ib7, ib8, ib9, ib10, ib14;
278  float Rmax0, Rmax1, wavmax0, wavmax1, ndvi;
279  float sipf, sicf, bair, mph0, mph1;
280  float *rhos = l2rec->l1rec->rhos;
281 
282  if ((l2rec->l1rec->l1file->sensorID != MERIS) &&
283  (l2rec->l1rec->l1file->sensorID != OLCIS3A) &&
284  (l2rec->l1rec->l1file->sensorID != OLCIS3B)) {
285  printf("MPH not supported for this sensor (%s).\n",
286  sensorId2SensorName(l2rec->l1rec->l1file->sensorID));
287  exit(EXIT_FAILURE);
288  }
289 
290  wav6 = 620;
291  wav7 = 664;
292  wav8 = 681;
293  wav9 = 709;
294  wav10 = 753;
295  wav14 = 885;
296 
297  ib6 = bindex_get(wav6);
298  ib7 = bindex_get(wav7);
299  ib8 = bindex_get(wav8);
300  ib9 = bindex_get(wav9);
301  ib10 = bindex_get(wav10);
302  ib14 = bindex_get(wav14);
303 
304  if (ib6 < 0 || ib7 < 0 || ib8 < 0 || ib9 < 0 || ib10 < 0 || ib14 < 0) {
305  printf("MPH_stumpf: incompatible sensor wavelengths for this algorithm\n");
306  exit(EXIT_FAILURE);
307  }
308 
309  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
310  ipb = l2rec->l1rec->l1file->nbands*ip;
311 
312  //if (l2rec->Rrs[ipb + ib6] <= 0.0) {
313  // if(l2rec->Rrs[ipb+ib6] <= 0.0 || l2rec->Rrs[ipb+ib7] <= 0.0 || l2rec->Rrs[ipb+ib8] <= 0.0
314  // || l2rec->Rrs[ipb+ib9] <= 0.0) {
315  //chl_mph[ip] = BAD_FLT;
316  //l2rec->l1rec->flags[ip] |= PRODFAIL;
317  //} else {
318  if (rhos[ipb + ib8] > rhos[ipb + ib9]) {
319  wavmax0 = wav8;
320  Rmax0 = rhos[ipb + ib8];
321  } else {
322  wavmax0 = wav9;
323  Rmax0 = rhos[ipb + ib9];
324  }
325  if (Rmax0 > rhos[ipb + ib10]) {
326  wavmax1 = wavmax0;
327  Rmax1 = Rmax0;
328  } else {
329  wavmax1 = wav10;
330  Rmax1 = rhos[ipb + ib10];
331  }
332 
333  //sun-induced phycocyanin absorption fluorescence
334  sipf = rhos[ipb + ib7] - rhos[ipb + ib6] - (rhos[ipb + ib8] - rhos[ipb + ib6]) * (664 - 619) / (681 - 619);
335  //sun induced chlorophyll fluorescence
336  sicf = rhos[ipb + ib8] - rhos[ipb + ib7] - (rhos[ipb + ib9] - rhos[ipb + ib7]) * (681 - 664) / (709 - 664);
337  //normalised difference vegetation index
338  ndvi = (rhos[ipb + ib14] - rhos[ipb + ib7]) / (rhos[ipb + ib14] + rhos[ipb + ib7]);
339  //backscatter and absorption induced reflectance
340  bair = rhos[ipb + ib9] - rhos[ipb + ib7] - (rhos[ipb + ib14] - rhos[ipb + ib7]) * (709 - 664) / (885 - 664);
341  mph0 = Rmax0 - rhos[ipb + ib7] - (rhos[ipb + ib14] - rhos[ipb + ib7]) * (wavmax0 - 664) / (885 - 664);
342  mph1 = Rmax1 - rhos[ipb + ib7] - (rhos[ipb + ib14] - rhos[ipb + ib7]) * (wavmax1 - 664) / (885 - 664);
343 
344  if (wavmax1 != wav10) {
345 
346  if (sicf >= 0 || sipf <= 0 || bair <= 0.002) {
347  chl_mph[ip] = 5.24e9 * pow(mph0, 4) - 1.95e8 * pow(mph0, 3) + 2.46e6 * pow(mph0, 2) + 4.02e3 * mph0 + 1.97;
348  } else {
349  chl_mph[ip] = 22.44 * exp(35.79 * mph1);
350  }
351  } else {
352  if (mph1 >= 0.02 || ndvi >= 0.2) {
353 
354  if (sicf < 0 && sipf > 0) {
355  chl_mph[ip] = 22.44 * exp(35.79 * mph1);
356 
357  } else {
358  chl_mph[ip] = BAD_FLT;
359  }
360  } else {
361  chl_mph[ip] = 5.24e9 * pow(mph0, 4) - 1.95e8 * pow(mph0, 3) + 2.46e6 * pow(mph0, 2) + 4.02e3 * mph0 + 1.97;
362  }
363  }
364  //}
365  //if (chl_mph[ip] < 0.) chl_mph[ip] = 0.;
366  }
367 
368 }
369 
370 uint8_t* get_flags_habs_mph(l2str *l2rec) {
371  //MPH - Maximum Peak Height
372  // from "Improved algorithm for routine monitoring of cyanobacteria and
373  // eutrophication in inland and near-coastal waters"
374  // Matthews and Odermatt, Remote Sensing of Environment (doi:10.1016/j.rse.2014.10.010)
375  //
376 
377  float wav6, wav7, wav8, wav9, wav10, wav14;
378  int ip, ipb, ib6, ib7, ib8, ib9, ib10, ib14;
379  float Rmax0, Rmax1, wavmax0, wavmax1, ndvi;
380  float sipf, sicf, bair, mph1, chl_mph;
381  float *rhos = l2rec->l1rec->rhos;
382  static float thresh = 350;
383 
384  if ((l2rec->l1rec->l1file->sensorID != MERIS) &&
385  (l2rec->l1rec->l1file->sensorID != OLCIS3A) &&
386  (l2rec->l1rec->l1file->sensorID != OLCIS3B)) {
387  printf("MPH not supported for this sensor (%s).\n",
388  sensorId2SensorName(l2rec->l1rec->l1file->sensorID));
389  exit(EXIT_FAILURE);
390  }
391 
392  allocateFlagsMPH(l2rec->l1rec->npix);
393 
394  wav6 = 620;
395  wav7 = 664;
396  wav8 = 681;
397  wav9 = 709;
398  wav10 = 753;
399  wav14 = 885;
400 
401  ib6 = bindex_get(wav6);
402  ib7 = bindex_get(wav7);
403  ib8 = bindex_get(wav8);
404  ib9 = bindex_get(wav9);
405  ib10 = bindex_get(wav10);
406  ib14 = bindex_get(wav14);
407 
408  if (ib6 < 0 || ib7 < 0 || ib8 < 0 || ib9 < 0 || ib10 < 0 || ib14 < 0) {
409  printf("MPH_stumpf: incompatible sensor wavelengths for this algorithm\n");
410  exit(EXIT_FAILURE);
411  }
412 
413  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
414 
415  flags_mph[ip] = 0;
416  ipb = l2rec->l1rec->l1file->nbands*ip;
417 
418  // if(l2rec->rhos[ipb+ib6] <= 0.0 ) {
419  if (l2rec->l1rec->rhos[ipb + ib6] <= 0.0 ||
420  l2rec->l1rec->rhos[ipb + ib7] <= 0.0 ||
421  l2rec->l1rec->rhos[ipb + ib8] <= 0.0 ||
422  l2rec->l1rec->rhos[ipb + ib9] <= 0.0 ||
423  (l2rec->l1rec->flags[ip] & LAND) != 0 ||
424  (l2rec->l1rec->flags[ip] & NAVFAIL) != 0) {
425  l2rec->l1rec->flags[ip] |= PRODFAIL;
426  flags_mph[ip] |= MPH_BADINPUT;
427  } else {
428  if (rhos[ipb + ib8] > rhos[ipb + ib9]) {
429  wavmax0 = wav8;
430  Rmax0 = rhos[ipb + ib8];
431  } else {
432  wavmax0 = wav9;
433  Rmax0 = rhos[ipb + ib9];
434  }
435  if (Rmax0 > rhos[ipb + ib10]) {
436  wavmax1 = wavmax0;
437  Rmax1 = Rmax0;
438  } else {
439  wavmax1 = wav10;
440  Rmax1 = rhos[ipb + ib10];
441  }
442 
443  //sun-induced phycocyanin absorption fluorescence
444  sipf = rhos[ipb + ib7] - rhos[ipb + ib6] - (rhos[ipb + ib8] - rhos[ipb + ib6]) * (664 - 619) / (681 - 619);
445  //sun induced chlorophyll fluorescence
446  sicf = rhos[ipb + ib8] - rhos[ipb + ib7] - (rhos[ipb + ib9] - rhos[ipb + ib7]) * (681 - 664) / (709 - 664);
447  //normalised difference vegetation index
448  ndvi = (rhos[ipb + ib14] - rhos[ipb + ib7]) / (rhos[ipb + ib14] + rhos[ipb + ib7]);
449  //backscatter and absorption induced reflectance
450  bair = rhos[ipb + ib9] - rhos[ipb + ib7] - (rhos[ipb + ib14] - rhos[ipb + ib7]) * (709 - 664) / (885 - 664);
451  //mph0 = Rmax0 - rhos[ipb+ib7] - ( rhos[ipb+ib14] - rhos[ipb+ib7]) * (wavmax0 - 664)/(885 - 664);
452  mph1 = Rmax1 - rhos[ipb + ib7] - (rhos[ipb + ib14] - rhos[ipb + ib7]) * (wavmax1 - 664) / (885 - 664);
453 
454  if (wavmax1 != wav10) {
455 
456  if (sicf < 0 && sipf > 0 && bair > 0.002) {
457  flags_mph[ip] |= MPH_CYANO;
458  chl_mph = 22.44 * exp(35.79 * mph1);
459  if (chl_mph > thresh)
460  flags_mph[ip] |= MPH_FLOAT;
461  }
462  } else {
463  if (mph1 >= 0.02 || ndvi >= 0.2) {
464  flags_mph[ip] |= MPH_FLOAT;
465 
466  if (sicf < 0 && sipf > 0) {
467  flags_mph[ip] |= MPH_CYANO;
468  chl_mph = 22.44 * exp(35.79 * mph1);
469  if (chl_mph > thresh)
470  flags_mph[ip] |= MPH_FLOAT;
471 
472  }
473  } else {
474  flags_mph[ip] |= MPH_ADJ;
475  }
476  }
477 
478  }
479 
480  }
481  return flags_mph;
482 }
483 
484 uint8_t* get_flags_habs_meris(l2str *l2rec) {
485  // Cloud Masking for MERIS & OLCI
486 
487  int ib443, ib490, ib510, ib560, ib620, ib665, ib681, ib709, ib754, ib865, ib885;
488  int ip, ipb;
489  float *rhos = l2rec->l1rec->rhos;
490  float mdsi, cv, mean, sum, sdev, w681_fac;
491  int i, n=7;
492 
493  allocateFlagsHABS(l2rec->l1rec->npix);
494 
495  if (l2rec->l1rec->l1file->sensorID != MERIS &&
496  l2rec->l1rec->l1file->sensorID != OLCIS3A &&
497  l2rec->l1rec->l1file->sensorID != OLCIS3B) {
498  printf("HABS flags not supported for this sensor (%s).\n",
499  sensorId2SensorName(l2rec->l1rec->l1file->sensorID));
500  exit(EXIT_FAILURE);
501  }
502 
503  ib443 = bindex_get(443);
504  ib490 = bindex_get(490);
505  ib510 = bindex_get(510);
506  ib560 = bindex_get(560);
507  ib620 = bindex_get(620);
508  ib665 = bindex_get(665);
509  ib681 = bindex_get(681);
510  ib709 = bindex_get(709);
511  ib754 = bindex_get(754);
512  ib865 = bindex_get(865);
513  ib885 = bindex_get(885);
514 
515  if (ib443 < 0 || ib490 < 0 || ib510 < 0 || ib560 < 0 || ib620 < 0 || ib665 < 0 || ib681 < 0 || ib709 < 0 || ib754 < 0 || ib865 < 0 || ib885 < 0) {
516  printf("get_flags_habs: incompatible sensor wavelengths for this algorithm\n");
517  exit(EXIT_FAILURE);
518  }
519 
520  if (l2rec->l1rec->l1file->sensorID == OLCIS3A ||
521  l2rec->l1rec->l1file->sensorID == OLCIS3B) {
522  w681_fac = 0.99;
523  } else {
524  w681_fac = 1.0;
525  }
526 
527  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
528  flags_habs[ip] = 0;
529  ipb = l2rec->l1rec->l1file->nbands*ip;
530 
531  // if the eval cloud masking is used, just use the cloud mask
532  // otherwise, run the get_cldmask function
533  if ((l1_input->evalmask | 2) == 2){
534  flags_habs[ip] = (uint8_t) l2rec->l1rec->cloud[ip];
535  } else {
536  if (get_cldmask(l2rec->l1rec, ip)) {
537  flags_habs[ip] |= HABS_CLOUD;
538  }
539  }
540 
541  if (rhos[ipb + ib443] >= 0.0 &&
542  rhos[ipb + ib490] >= 0.0 &&
543  rhos[ipb + ib510] >= 0.0 &&
544  rhos[ipb + ib560] >= 0.0 &&
545  rhos[ipb + ib620] >= 0.0 &&
546  rhos[ipb + ib665] >= 0.0 &&
547  rhos[ipb + ib681] >= 0.0 &&
548  rhos[ipb + ib709] >= 0.0 &&
549  rhos[ipb + ib754] >= 0.0 &&
550  rhos[ipb + ib885] >= 0.0) {
551 
552  if (rhos[ipb + ib885] > rhos[ipb + ib620] &&
553  rhos[ipb + ib885] > rhos[ipb + ib709] &&
554  rhos[ipb + ib885] > rhos[ipb + ib754] &&
555  rhos[ipb + ib885] > 0.01) {
556  flags_habs[ip] |= HABS_NONWTR;
557  }
558 
559  // test dry lake condition: (rhos_620 > rhos_560)
560  if ((rhos[ipb + ib620] > rhos[ipb + ib560]) &&
561  (rhos[ipb + ib560] > 0.15) &&
562  (rhos[ipb + ib885] > 0.15)){
563  flags_habs[ip] |= HABS_NONWTR;
564  }
565 
566  // test snow/ice condition - meris differential snow index
567  mdsi = (rhos[ipb + ib865] - rhos[ipb + ib885]) / (rhos[ipb + ib865] + rhos[ipb + ib885]);
568 
569  // test snow/ice condition - exclude potential bloom conditions (based on coefficient of variation in visible bands)
570  int b[7] = {ib443, ib490, ib510, ib560, ib620, ib665, ib681};
571  sum = 0;
572  for(i = 0; i < n; ++i) {
573  sum += rhos[ipb + b[i]];
574  }
575  mean = sum / n;
576 
577  sdev = 0;
578  for(i = 0; i < n; ++i) {
579  sdev += pow((rhos[ipb + b[i]] - mean),2);
580  }
581  cv = sqrt(sdev / (n - 1)) / mean;
582 
583  if ((mdsi > 0.01) && (rhos[ipb + ib885] > 0.15) && (cv < 0.1)) {
584  flags_habs[ip] |= HABS_SNOWICE;
585  }
586  //adjacency flagging
587  float mci = 1.0 * (rhos[ipb + ib709] - rhos[ipb + ib681]*w681_fac
588  + (rhos[ipb + ib681]*w681_fac - rhos[ipb + ib754])*(709 - 681) / (754 - 681));
589  float ci = 1.0 * ((rhos[ipb + ib709] - rhos[ipb + ib665])*(681 - 665) / (709 - 665)
590  - (rhos[ipb + ib681]*w681_fac - rhos[ipb + ib665]));
591 
592  habs_meris_ci_corr(rhos[ipb + ib443], rhos[ipb + ib490],
593  rhos[ipb + ib560], rhos[ipb + ib620],
594  rhos[ipb + ib665], rhos[ipb + ib709],
595  rhos[ipb + ib865], rhos[ipb + ib885], &ci);
596 
597  if ((mci < 0) && (ci > 0)) {
598  flags_habs[ip] |= HABS_ADJ;
599  }
600  } else {
601  if (rhos[ipb + ib620] < 0.0 ||
602  rhos[ipb + ib665] < 0.0 ||
603  rhos[ipb + ib681] < 0.0 ||
604  rhos[ipb + ib709] < 0.0) {
605  flags_habs[ip] |= HABS_BADINPUT;
606  }
607  }
608  }
609  return flags_habs;
610 }
611 
612 uint8_t* get_flags_habs_modis(l2str *l2rec) {
613  // Cloud Masking for MODIS
614 
615  int ib469, ib555, ib645, ib667, ib859, ib1240, ib2130;
616  int ip, ipb;
617  float *rhos = l2rec->l1rec->rhos, *Rrs = l2rec->Rrs, *cloud_albedo = l2rec->l1rec->cloud_albedo;
618  float ftemp, ftemp2, ftemp3;
619  float cloudthr = 0.027;
620 
621  allocateFlagsHABS(l2rec->l1rec->npix);
622 
623  if (l2rec->l1rec->l1file->sensorID != MODISA && l2rec->l1rec->l1file->sensorID != MODIST) {
624  printf("HABS flags not supported for this sensor (%s).\n",
625  sensorId2SensorName(l2rec->l1rec->l1file->sensorID));
626  exit(EXIT_FAILURE);
627  }
628 
629  ib469 = bindex_get(469);
630  ib555 = bindex_get(555);
631  ib645 = bindex_get(645);
632  ib667 = bindex_get(667);
633  ib859 = bindex_get(859);
634  ib1240 = bindex_get(1240);
635  ib2130 = bindex_get(2130);
636 
637  if (ib469 < 0 || ib555 < 0 || ib645 < 0 || ib667 < 0 || ib859 < 0 || ib1240 < 0 || ib2130 < 0) {
638  printf("get_flags_habs: incompatible sensor wavelengths for this algorithm\n");
639  exit(EXIT_FAILURE);
640  }
641 
642  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
643  flags_habs[ip] = 0;
644  ipb = l2rec->l1rec->l1file->nbands*ip;
645  // TODO: make consistent with get_cloudmask_modis and use it instead of
646  // this duplication of code...
647 
648  if (l2rec->chl[ip] < 0)
649  ftemp = Rrs[ipb + ib667];
650  else
651  ftemp = Rrs[ipb + ib667]*(0.45 + l2rec->chl[ip]*0.005) / 4.3;
652  // first correct for turbid water
653  if (Rrs[ipb + ib667] < 0.0) ftemp = 0.0;
654  ftemp2 = cloud_albedo[ip] - ftemp;
655 
656  if (ftemp2 > 0.027) flags_habs[ip] |= HABS_CLOUD;
657  // non-water check 1240 is bright relative to 859 and the combination is bright
658  // this may hit glint by accident, need to be checked.
659 
660  if (rhos[ipb + ib1240] / rhos[ipb + ib859] > 0.5 && (rhos[ipb + ib1240] + rhos[ipb + ib2130]) > 0.10) flags_habs[ip] |= HABS_CLOUD;
661 
662  // now try to correct for glint
663  // region check was thrown out {IF (region = "OM") cloudthr = 0.04} rjh 11/2/2015
664 
665  ftemp = rhos[ipb + ib645] - rhos[ipb + ib555] + (rhos[ipb + ib555] - rhos[ipb + ib859])*(645.0 - 555.0) / (859.0 - 555.0);
666  ftemp2 = cloud_albedo[ip] + ftemp;
667  if (ftemp2 < cloudthr) flags_habs[ip] = 0;
668  if (rhos[ipb + ib859] / rhos[ipb + ib1240] > 4.0) flags_habs[ip] = 0;
669 
670  // scum areas
671 
672  if ((rhos[ipb + ib859] - rhos[ipb + ib469]) > 0.01 && cloud_albedo[ip] < 0.30) flags_habs[ip] = 0;
673  if ((rhos[ipb + ib859] - rhos[ipb + ib645]) > 0.01 && cloud_albedo[ip] < 0.15) flags_habs[ip] = 0;
674  if (rhos[ipb + ib1240] < 0.2)
675  ftemp2 = ftemp2 - (rhos[ipb + ib859] - rhos[ipb + ib1240]) * fabs(rhos[ipb + ib859] - rhos[ipb + ib1240]) / cloudthr;
676 
677  ftemp3 = ftemp2;
678  if (ftemp2 < cloudthr * 2) {
679  if ((rhos[ipb + ib555] - rhos[ipb + ib1240]) > (rhos[ipb + ib469] - rhos[ipb + ib1240])) {
680  ftemp3 = ftemp2 - (rhos[ipb + ib555] - rhos[ipb + ib1240]);
681  } else {
682  ftemp3 = ftemp2 - (rhos[ipb + ib469] - rhos[ipb + ib1240]);
683  }
684  }
685 
686  if (ftemp3 < cloudthr) flags_habs[ip] = 0;
687 
688  if (rhos[ipb + ib555] >= 0 && rhos[ipb + ib1240] >= 0.0 && rhos[ipb + ib1240] > rhos[ipb + ib555])
689  flags_habs[ip] |= HABS_NONWTR;
690  }
691  return flags_habs;
692 }
693 
694 uint8_t* get_flags_habs(l2str *l2rec) {
695  static int32_t currentLine = -1;
696 
697  if (currentLine == l2rec->l1rec->iscan )
698  return flags_habs;
699  currentLine = l2rec->l1rec->iscan;
700  switch (l2rec->l1rec->l1file->sensorID) {
701  case MERIS:
702  case OLCIS3A:
703  case OLCIS3B:
704  return get_flags_habs_meris(l2rec);
705  break;
706  case MODISA:
707  case MODIST:
708  return get_flags_habs_modis(l2rec);
709  break;
710  default:
711  printf("HABS flags not supported for this sensor (%s).\n",
712  sensorId2SensorName(l2rec->l1rec->l1file->sensorID));
713  exit(EXIT_FAILURE);
714  }
715  return NULL;
716 }
char get_cldmask(l1str *l1rec, int32_t ip)
Definition: cloud_flag.c:322
void get_habs_mph(l2str *l2rec, l2prodstr *p, float chl_mph[])
Definition: get_habs.c:275
#define OLCIS3A
Definition: sensorDefs.h:32
void freeProductInfo(productInfo_t *info)
#define CAT_CI_noncyano
Definition: l2prod.h:368
#define HABS_ADJ
Definition: mph_flags.h:21
#define HABS_CLOUD
Definition: mph_flags.h:18
float mean(float *xs, int sample_size)
Definition: numerical.c:81
#define CAT_MCI_stumpf
Definition: l2prod.h:324
#define NULL
Definition: decode_rs.h:63
#define MERIS
Definition: sensorDefs.h:22
#define HABS_NONWTR
Definition: mph_flags.h:19
#define MODIST
Definition: sensorDefs.h:18
void get_habs_ci(l2str *l2rec, l2prodstr *p, float ci[])
Definition: get_habs.c:85
#define MPH_BADINPUT
Definition: mph_flags.h:15
#define PRODFAIL
Definition: l2_flags.h:41
uint8_t * get_flags_habs_meris(l2str *l2rec)
Definition: get_habs.c:484
instr * input
#define fac
uint8_t * get_flags_habs(l2str *l2rec)
Definition: get_habs.c:694
#define HABS_BADINPUT
Definition: mph_flags.h:22
int bindex_get(int32_t wave)
Definition: windex.c:45
productInfo_t * allocateProductInfo()
#define CAT_CI_stumpf
Definition: l2prod.h:323
#define MPH_FLOAT
Definition: mph_flags.h:12
l1_input_t * l1_input
Definition: l1_options.c:9
#define MPH_ADJ
Definition: mph_flags.h:14
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 for both the L1A and Geolocation enabling retrieval of South Polar DEM data Resolved Bug by changing to opent the geolocation file only after a successful read of the L1A and also by checking for fatal errors from not restoring C5 and to report how many of those high resolution values were water in the new WaterPresent SDS Added valid_range attribute to Land SeaMask Changed to bilinearly interpolate the geoid_height to remove artifacts at one degree lines Made corrections to const qualification of pointers allowed by new version of M API library Removed casts that are no longer for same not the geoid Corrected off by one error in calculation of high resolution offsets Corrected parsing of maneuver list configuration parameter Corrected to set Height SDS to fill values when geolocation when for elevation and land water mask
Definition: HISTORY.txt:114
void allocateFlagsHABS(int numPix)
Definition: get_habs.c:42
#define LAND
Definition: l2_flags.h:12
int findProductInfo(const char *productName, int sensorId, productInfo_t *info)
uint8_t * get_flags_habs_modis(l2str *l2rec)
Definition: get_habs.c:612
data_t b[NROOTS+1]
Definition: decode_rs.h:77
void allocateFlagsMPH(int numPix)
Definition: get_habs.c:34
#define BAD_FLT
Definition: jplaeriallib.h:19
#define MPH_CYANO
Definition: mph_flags.h:13
const char * sensorId2SensorName(int sensorId)
Definition: sensorInfo.c:273
#define fabs(a)
Definition: misc.h:93
#define HABS_SNOWICE
Definition: mph_flags.h:20
#define OLCIS3B
Definition: sensorDefs.h:41
#define CAT_CI_cyano
Definition: l2prod.h:367
int i
Definition: decode_rs.h:71
void habs_meris_ci_corr(float rhos443, float rhos490, float rhos560, float rhos620, float rhos665, float rhos709, float rhos865, float rhos885, float *ci)
Definition: get_habs.c:51
uint8_t * get_flags_habs_mph(l2str *l2rec)
Definition: get_habs.c:370
#define MODISA
Definition: sensorDefs.h:19
float p[MODELMAX]
Definition: atrem_corl1.h:131
#define NAVFAIL
Definition: l2_flags.h:36