NASA Logo
Ocean Color Science Software

ocssw V2022
get_ndvi.c
Go to the documentation of this file.
1 /*---------------------------------------------------------------------*/
2 /* get_ndvi.c - vegetation index classification for MSl12. */
3 /* */
4 /* Inputs: */
5 /* l2rec - level-2 structure containing one complete scan after */
6 /* atmospheric correction. */
7 /* Outputs: */
8 /* ndvi - vegetation index for land, 1 value per pixel. */
9 /* */
10 /* Written by: Bryan Franz, SAIC-GSC, February 2000 */
11 /* Jakob Lindo, SSAI, February 2024 */
12 /* */
13 /*---------------------------------------------------------------------*/
14 
15 #include <stdlib.h>
16 #include <math.h>
17 #include "l12_proto.h"
18 #include "vegetation_indices.h"
19 
20 static int idx_red_min = BAD_INT;
21 static int idx_red_max = BAD_INT;
22 static int idx_nir_min = BAD_INT;
23 static int idx_nir_max = BAD_INT;
24 static int idx_blu_min = BAD_INT;
25 static int idx_blu_max = BAD_INT;
26 
27 // Multi-band constants
28 static float undef = BAD_FLT;
29 static int ibblue = -1;
30 static int ibred = -1;
31 static int ibnir = -1;
32 
33 // as per C. Tucker (11/2014), no cut-off at -2
34 static float minval = -1000.0;
35 static float maxval = 1000.0;
36 static int32_t mask = LAND;
37 
38 static int32_t sensor = -1; // An enum indicating which sensor was used
39 static int32_t num_bands; // Number of bands the instrument used has
40 
46 void get_ndvi(l1str *l1rec, float ndvi[]) {
47  int32_t idx_pixel = -0; // The index of the current pixel
48  int32_t idx_band = -0; // Offset from l1rec->l1file->nbands; start of this pixel's bands
49  float red = -0.0;
50  float nir = -0.0;
51 
52  if ((!instrument_is_hyperspectral(num_bands) && (ibred < 0 || ibnir < 0)) ||
53  (instrument_is_hyperspectral(num_bands) &&
54  (idx_red_min < 0 || idx_nir_min < 0 || idx_red_max < 0 || idx_nir_max < 0))) {
55  printf("NDVI requires bands near 670 and 865nm\n");
56  exit(1);
57  }
58 
59  for (idx_pixel = 0; idx_pixel < l1rec->npix; idx_pixel++) {
60  ndvi[idx_pixel] = undef;
61  idx_band = l1rec->l1file->nbands * idx_pixel;
62 
63  if (instrument_is_hyperspectral(num_bands)) {
64  int red_width = idx_red_max - idx_red_min + 1; // Difference == width - 1; add 1 back
65  int nir_width = idx_nir_max - idx_nir_min + 1; // Difference == width - 1; add 1 back
66  float *start_of_rhos_red = &l1rec->rhos[idx_band + idx_red_min];
67  float *start_of_rhos_nir = &l1rec->rhos[idx_band + idx_nir_min];
68 
69  red = average_rhos_values(start_of_rhos_red, red_width); // Average rho_s b/n 620 and 670
70  nir = average_rhos_values(start_of_rhos_nir, nir_width); // Average rho_s b/n 841 and 876
71  } else {
72  red = l1rec->rhos[idx_band + ibred];
73  nir = l1rec->rhos[idx_band + ibnir];
74  }
75 
76  double rhos_values[] = {red, nir};
77  if (invalid_pixel(l1rec->dem[idx_pixel], l1rec->flags[idx_pixel] & mask, rhos_values, 2)) {
78  l1rec->flags[idx_pixel] |= PRODFAIL;
79  continue;
80  }
81  float ndvi_value = (nir - red) / (nir + red);
82  ndvi[idx_pixel] = clamp(ndvi_value, (int)minval, (int)maxval);
83  }
84 }
85 
91 void get_evi(l1str *l1rec, float evi[]) {
92  static float L = 1.0, c1 = 6.0, c2 = 7.5;
93 
94  int32_t idx_pixel = -0; // The index of the current pixel
95  int32_t idx_band = -0; // Offset from l1rec->l1file->nbands; start of this pixel's bands
96  float blu = -0.0;
97  float red = -0.0;
98  float nir = -0.0;
99  double val = -0.0;
100 
101  if ((!instrument_is_hyperspectral(num_bands) && (ibblue < 0 || ibred < 0 || ibnir < 0)) ||
102  (instrument_is_hyperspectral(num_bands) && (idx_red_min < 0 || idx_red_max < 0 || idx_nir_min < 0 ||
103  idx_nir_max < 0 || idx_blu_max < 0 || idx_blu_max < 0))) {
104  printf("EVI requires bands near 412, 670, and 865nm\n");
105  exit(1);
106  }
107 
108  for (idx_pixel = 0; idx_pixel < l1rec->npix; idx_pixel++) {
109  evi[idx_pixel] = undef;
110  idx_band = l1rec->l1file->nbands * idx_pixel;
111 
112  if (instrument_is_hyperspectral(num_bands)) {
113  int blu_width = idx_blu_max - idx_blu_min + 1; // Difference == width - 1; add 1 back
114  int red_width = idx_red_max - idx_red_min + 1; // Difference == width - 1; add 1 back
115  int nir_width = idx_nir_max - idx_nir_min + 1; // Difference == width - 1; add 1 back
116  float *start_of_rhos_blu = &l1rec->rhos[idx_band + idx_blu_max];
117  float *start_of_rhos_red = &l1rec->rhos[idx_band + idx_red_min];
118  float *start_of_rhos_nir = &l1rec->rhos[idx_band + idx_nir_min];
119 
120  blu = average_rhos_values(start_of_rhos_blu, blu_width); // Average rho_s b/n 455 and 480
121  red = average_rhos_values(start_of_rhos_red, red_width); // Average rho_s b/n 565 and 670
122  nir = average_rhos_values(start_of_rhos_nir, nir_width); // Average rho_s b/n 840 and 875
123  } else {
124  blu = l1rec->rhos[idx_band + ibblue];
125  red = l1rec->rhos[idx_band + ibred];
126  nir = l1rec->rhos[idx_band + ibnir];
127  }
128 
129  double rhos_values[] = {red, nir, blu};
130  if (invalid_pixel(l1rec->dem[idx_pixel], l1rec->flags[idx_pixel] & mask, rhos_values, 3)) {
131  l1rec->flags[idx_pixel] |= PRODFAIL;
132  continue;
133  }
134  double evi_val = 0xdeadbeef;
135  if (blu > 0.0 && (blu <= red || red <= nir)) {
136  /* Most cases - EVI formula */
137 
138  if ((val = L + nir + c1 * red - c2 * blu) == 0)
139  continue;
140  else
141  evi_val = 2.5 * (nir - red) / val;
142 
143  } else {
144  /* Backup - SAVI formula */
145 
146  if ((val = 0.5 + nir + red) == 0)
147  continue;
148  else
149  evi_val = 1.5 * (nir - red) / val;
150  }
151  evi[idx_pixel] = clamp(evi_val, minval, maxval);
152  }
153 }
154 
155 // From Compton Tucker 07/30/2016
156 // EVI3=2.5*(nir-red)/(nir+6*red-7.5*blue+1)
157 // EVI2=2.5*(NIR-Red)/(NIR+2.4*Red+1)
158 
164 void get_evi2(l1str *l1rec, float evi2[]) {
165  int32_t idx_pixel = -0; // The index of the current pixel
166  int32_t idx_band = -0; // Offset from l1rec->l1file->nbands; start of this pixel's bands
167  float red;
168  float nir;
169 
170  if ((!instrument_is_hyperspectral(num_bands) && (ibred < 0 || ibnir < 0)) ||
171  (instrument_is_hyperspectral(num_bands) &&
172  (idx_red_min < 0 || idx_nir_min < 0 || idx_red_max < 0 || idx_nir_max < 0))) {
173  printf("EVI2 requires bands near 670, and 865nm\n");
174  exit(1);
175  }
176 
177  for (idx_pixel = 0; idx_pixel < l1rec->npix; idx_pixel++) {
178  evi2[idx_pixel] = undef;
179  idx_band = l1rec->l1file->nbands * idx_pixel;
180 
181  if (instrument_is_hyperspectral(num_bands)) {
182  int red_width = idx_red_max - idx_red_min + 1; // Difference == width - 1; add 1 back
183  int nir_width = idx_nir_max - idx_nir_min + 1; // Difference == width - 1; add 1 back
184  float *start_of_rhos_red = &l1rec->rhos[idx_band + idx_red_min];
185  float *start_of_rhos_nir = &l1rec->rhos[idx_band + idx_nir_min];
186 
187  red = average_rhos_values(start_of_rhos_red, red_width); // Average rho_s b/n 565 and 670
188  nir = average_rhos_values(start_of_rhos_nir, nir_width); // Average rho_s b/n 840 and 875
189  } else {
190  red = l1rec->rhos[idx_band + ibred];
191  nir = l1rec->rhos[idx_band + ibnir];
192  }
193 
194  double rhos_values[] = {red, nir};
195  if (invalid_pixel(l1rec->dem[idx_pixel], l1rec->flags[idx_pixel] & mask, rhos_values, 2)) {
196  l1rec->flags[idx_pixel] |= PRODFAIL;
197  continue;
198  }
199  double evi2_val = -0.0;
200  if (red <= nir) { // Wouldn't this always be the case?
201  evi2_val = 2.5 * (nir - red) / (nir + 2.4 * red + 1);
202  }
203  evi2[idx_pixel] = clamp(evi2_val, minval, maxval);
204  }
205 }
206 
212 void get_evi3(l1str *l1rec, float evi3[]) {
213  static float L = 1.0, c1 = 6.0, c2 = 7.5;
214 
215  int32_t idx_pixel = -0; // The index of the current pixel
216  int32_t idx_band = -0; // Offset from l1rec->l1file->nbands; start of this pixel's bands
217  float blu;
218  float red;
219  float nir;
220  double val;
221 
222  if ((!instrument_is_hyperspectral(num_bands) && (ibblue < 0 || ibred < 0 || ibnir < 0)) ||
223  (instrument_is_hyperspectral(num_bands) && (idx_red_min < 0 || idx_red_max < 0 || idx_nir_min < 0 ||
224  idx_nir_max < 0 || idx_blu_max < 0 || idx_blu_max < 0))) {
225  printf("EVI requires bands near 412, 670, and 865nm\n");
226  exit(1);
227  }
228 
229  for (idx_pixel = 0; idx_pixel < l1rec->npix; idx_pixel++) {
230  evi3[idx_pixel] = undef;
231  idx_band = l1rec->l1file->nbands * idx_pixel;
232 
233  if (instrument_is_hyperspectral(num_bands)) {
234  int blu_width = idx_blu_max - idx_blu_min + 1; // Difference == width - 1; add 1 back
235  int red_width = idx_red_max - idx_red_min + 1; // Difference == width - 1; add 1 back
236  int nir_width = idx_nir_max - idx_nir_min + 1; // Difference == width - 1; add 1 back
237  float *start_of_rhos_blu = &l1rec->rhos[idx_band + idx_blu_max];
238  float *start_of_rhos_red = &l1rec->rhos[idx_band + idx_red_min];
239  float *start_of_rhos_nir = &l1rec->rhos[idx_band + idx_nir_min];
240 
241  blu = average_rhos_values(start_of_rhos_blu, blu_width); // Average rho_s b/n 455 and 480
242  red = average_rhos_values(start_of_rhos_red, red_width); // Average rho_s b/n 565 and 670
243  nir = average_rhos_values(start_of_rhos_nir, nir_width); // Average rho_s b/n 840 and 875
244  } else {
245  blu = l1rec->rhos[idx_band + ibblue];
246  red = l1rec->rhos[idx_band + ibred];
247  nir = l1rec->rhos[idx_band + ibnir];
248  }
249 
250  double rhos_values[] = {red, nir, blu};
251  if (invalid_pixel(l1rec->dem[idx_pixel], l1rec->flags[idx_pixel] & mask, rhos_values, 3)) {
252  l1rec->flags[idx_pixel] |= PRODFAIL;
253  continue;
254  }
255 
256  double evi3_val = -0.0;
257  if (blu > 0.0 && (blu <= red || red <= nir)) {
258  /* Most cases - EVI formula */
259 
260  if ((val = L + nir + c1 * red - c2 * blu) == 0)
261  continue;
262  else
263  evi3_val = 2.5 * (nir - red) / val;
264  }
265  evi3[idx_pixel] = clamp(evi3_val, minval, maxval);
266  }
267 }
268 
269 void get_ndvi_evi(l1str *l1rec, int prodnum, float prod[]) {
270  sensor = l1rec->l1file->sensorID;
271  num_bands = l1rec->l1file->nbands;
272 
273  static bool firstCall = true;
274  if (firstCall) {
275  firstCall = false;
276  if (instrument_is_hyperspectral(num_bands)) {
277  idx_blu_min = bindex_get(blu_min);
278  idx_blu_max = bindex_get(blu_max);
279  idx_red_min = bindex_get(red_min);
280  idx_red_max = bindex_get(red_max);
281  idx_nir_min = bindex_get(nir_min);
282  idx_nir_max = bindex_get(nir_max);
283  } else {
284  ibblue = bindex_get(412);
285  ibred = bindex_get(670);
286  ibnir = bindex_get(865);
287  }
288 
289  if (sensor == MODISA || sensor == MODIST) {
290  ibred = bindex_get(645);
291  ibnir = bindex_get(859);
292  }
293  }
294 
295  switch (prodnum) {
296  case CAT_ndvi:
297  get_ndvi(l1rec, prod);
298  break;
299  case CAT_evi:
300  get_evi(l1rec, prod);
301  break;
302  case CAT_evi2:
303  get_evi2(l1rec, prod);
304  break;
305  case CAT_evi3:
306  get_evi3(l1rec, prod);
307  break;
308  default:
309  printf("Error: %s : Unknown product specifier: %d\n", __FILE__, prodnum);
310  exit(FATAL_ERROR);
311  }
312 }
void red(int iz1, int iz2, int jz1, int jz2, int jm1, int jm2, int jmf, int ic1, int jc1, int jcf, int kc, float ***c, float **s)
#define L(lambda, T)
Definition: PreprocessP.h:185
#define CAT_evi2
Definition: l2prod.h:73
float average_rhos_values(float rhos_values[], size_t length)
Get the average of rho_s values from a hyperspectral measurement to approximate a multi-band measurem...
read l1rec
void get_ndvi(l1str *l1rec, float ndvi[])
Calculate Normalized Difference Vegetation Index CAT_ix 32.
Definition: get_ndvi.c:46
const int32_t blu_min
#define MODIST
Definition: sensorDefs.h:18
#define PRODFAIL
Definition: l2_flags.h:41
void get_evi(l1str *l1rec, float evi[])
Calculate Enhanced Vegetation Index CAT_ix 38.
Definition: get_ndvi.c:91
int bindex_get(int32_t wave)
Definition: windex.c:45
void get_ndvi_evi(l1str *l1rec, int prodnum, float prod[])
Main entry point for getting NDVI/EVI producs.
Definition: get_ndvi.c:269
bool invalid_pixel(double pixel_elevation, double pixel_mask, double rhos_values[], int len_rhos_values)
Check pixel attributes for validity.
#define CAT_evi
Definition: l2prod.h:45
#define CAT_evi3
Definition: l2prod.h:74
#define FATAL_ERROR
Definition: swl0_parms.h:5
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
bool instrument_is_hyperspectral(int32_t num_bands)
const int32_t red_min
#define LAND
Definition: l2_flags.h:12
void get_evi3(l1str *l1rec, float evi3[])
Calculate Enhanced Vegetation Index - EVI3 CAT_ix 64.
Definition: get_ndvi.c:212
const int32_t red_max
#define BAD_FLT
Definition: jplaeriallib.h:19
const int32_t nir_min
#define BAD_INT
Definition: genutils.h:23
const int32_t nir_max
float clamp(float pixel_value, int minimum, int maximum)
Clamp the value of a pixel between minval and maxval.
#define CAT_ndvi
Definition: l2prod.h:39
#define MODISA
Definition: sensorDefs.h:19
void get_evi2(l1str *l1rec, float evi2[])
Calculate Enhanced Vegetation Index - EVI2 CAT_ix 63.
Definition: get_ndvi.c:164
const int32_t blu_max