Пример #1
0
/******************************************************************************
METHOD:  l8cfmask

PURPOSE:  the main routine for fmask in C

RETURN VALUE:
Type = int
Value           Description
-----           -----------
ERROR           An error occurred during processing of the l8cfmask
SUCCESS         Processing was successful

PROJECT:  Land Satellites Data System Science Research and Development (LSRD)
at the USGS EROS

HISTORY:
Date        Programmer       Reason
--------    ---------------  -------------------------------------
3/15/2013   Song Guo         Original Development
5/14/2013   Song Guo         Added in Polar Stereographic support
7/17/2013   Song Guo         Added in Map info 
8/15/2013   Song Guo         Modified to use TOA reflectance file 
                             as input instead of metadata file
Oct/2014    Ron Dilley       Modified to utilize the ESPA internal raw binary
                             file format

NOTES: type ./l8cfmask --help for information to run the code
******************************************************************************/
int
main (int argc, char *argv[])
{
    char errstr[MAX_STR_LEN];     /* error string */
    char *cptr = NULL;            /* pointer to the file extension */
    char *xml_name = NULL;        /* input XML filename */
    char directory[MAX_STR_LEN];  /* input/output data directory */
    char extension[MAX_STR_LEN];  /* input TOA file extension */
    int ib;                       /* band counters */
    Input_t *input = NULL;        /* input data and meta data */
    char envi_file[MAX_STR_LEN];  /* output ENVI file name */
    char scene_name[MAX_STR_LEN]; /* input data scene name */
    unsigned char **pixel_mask;   /* pixel mask */
    unsigned char **conf_mask;    /* confidence mask */
    int status;               /* return value from function call */
    float clear_ptm;          /* percent of clear-sky pixels */
    float t_templ = 0.0;      /* percentile of low background temperature */
    float t_temph = 0.0;      /* percentile of high background temperature */
    Output_t *output = NULL;  /* output structure and metadata */
    bool verbose;             /* verbose flag for printing messages */
    bool use_l8_cirrus;       /* should we use L8 cirrus cloud bit results? */
    int cldpix = 2;           /* Default buffer for cloud pixel dilate */
    int sdpix = 2;            /* Default buffer for shadow pixel dilate */
    float cloud_prob;         /* Default cloud probability */
    float sun_azi_temp = 0.0; /* Keep the original sun azimuth angle */
    int max_cloud_pixels; /* Maximum cloud pixel number in cloud division */
    Espa_internal_meta_t xml_metadata; /* XML metadata structure */
    Envi_header_t envi_hdr;            /* output ENVI header information */

    time_t now;
    time (&now);
    printf ("CFmask start_time=%s\n", ctime (&now));

    /* Read the command-line arguments, including the name of the input
       Landsat TOA reflectance product and the DEM */
    status = get_args (argc, argv, &xml_name, &cloud_prob, &cldpix,
                       &sdpix, &max_cloud_pixels, &use_l8_cirrus, &verbose);
    if (status != SUCCESS)
    {
        sprintf (errstr, "calling get_args");
        CFMASK_ERROR (errstr, "main");
    }

    /* Validate the input metadata file */
    if (validate_xml_file (xml_name) != SUCCESS)
    {                           /* Error messages already written */
        CFMASK_ERROR (errstr, "main");
    }

    /* Initialize the metadata structure */
    init_metadata_struct (&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata (xml_name, &xml_metadata) != SUCCESS)
    {                           /* Error messages already written */
        CFMASK_ERROR (errstr, "main");
    }

    /* Verify supported satellites */
    if (strcmp (xml_metadata.global.satellite, "LANDSAT_8") != 0)
    {
        sprintf (errstr, "Unsupported satellite sensor");
        CFMASK_ERROR (errstr, "main");
    }

    /* Split the filename to obtain the directory, scene name, and extension */
    split_filename (xml_name, directory, scene_name, extension);
    if (verbose)
        printf ("directory, scene_name, extension=%s,%s,%s\n",
                directory, scene_name, extension);

    /* Open input file, read metadata, and set up buffers */
    input = OpenInput (&xml_metadata);
    if (input == NULL)
    {
        sprintf (errstr, "opening the TOA and brightness temp files in: %s",
                 xml_name);
        CFMASK_ERROR (errstr, "main");
    }

    if (verbose)
    {
        /* Print some info to show how the input metadata works */
        printf ("DEBUG: Number of input TOA bands: %d\n", input->nband);
        printf ("DEBUG: Number of input thermal bands: %d\n", 1);
        printf ("DEBUG: Number of input TOA lines: %d\n", input->size.l);
        printf ("DEBUG: Number of input TOA samples: %d\n", input->size.s);
        printf ("DEBUG: ACQUISITION_DATE.DOY is %d\n",
                input->meta.acq_date.doy);
        printf ("DEBUG: Fill value is %d\n", input->meta.fill);
        for (ib = 0; ib < input->nband; ib++)
        {
            printf ("DEBUG: Band %d-->\n", ib);
            printf ("DEBUG:   band satu_value_ref: %d\n",
                    input->meta.satu_value_ref[ib]);
            printf ("DEBUG:   band satu_value_max: %d\n",
                    input->meta.satu_value_max[ib]);
            printf ("DEBUG:   band gains: %f, band biases: %f\n",
                    input->meta.gain[ib], input->meta.bias[ib]);
        }
        printf ("DEBUG: Thermal Band (Band 10) -->\n");
        printf ("DEBUG:   therm_satu_value_ref: %d\n",
                input->meta.therm_satu_value_ref);
        printf ("DEBUG:   therm_satu_value_max: %d\n",
                input->meta.therm_satu_value_max);
        printf ("DEBUG:   therm_gain: %f, therm_bias: %f\n",
                input->meta.gain_th, input->meta.bias_th);

        printf ("DEBUG: SUN AZIMUTH is %f\n", input->meta.sun_az);
        printf ("DEBUG: SUN ZENITH is %f\n", input->meta.sun_zen);
    }

    /* If the scene is an ascending polar scene (flipped upside down), then
       the solar azimuth needs to be adjusted by 180 degrees.  The scene in
       this case would be north down and the solar azimuth is based on north
       being up clock-wise direction. Flip the south to be up will not change
       the actual sun location, with the below relations, the solar azimuth
       angle will need add in 180.0 for correct sun location */
    if (input->meta.ul_corner.is_fill &&
        input->meta.lr_corner.is_fill &&
        (input->meta.ul_corner.lat - input->meta.lr_corner.lat) < MINSIGMA)
    {
        /* Keep the original solar azimuth angle */
        sun_azi_temp = input->meta.sun_az;
        input->meta.sun_az += 180.0;
        if ((input->meta.sun_az - 360.0) > MINSIGMA)
            input->meta.sun_az -= 360.0;
        if (verbose)
            printf ("  Polar or ascending scene."
                    "  Readjusting solar azimuth by 180 degrees.\n"
                    "  New value: %f degrees\n", input->meta.sun_az);
    }

    /* Dynamic allocate the 2d mask memory */
    pixel_mask = (unsigned char **) allocate_2d_array (input->size.l,
                                                       input->size.s,
                                                       sizeof (unsigned char));
    if (pixel_mask == NULL)
    {
        sprintf (errstr, "Allocating pixel mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    conf_mask = (unsigned char **) allocate_2d_array (input->size.l,
                                                      input->size.s,
                                                      sizeof (unsigned char));
    if (conf_mask == NULL)
    {
        sprintf (errstr, "Allocating confidence mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    /* Initialize the mask to clear data */
    int row, col;
    for (row = 0; row < input->size.l; row++)
        for (col = 0; col < input->size.s; col++)
        {
            pixel_mask[row][col] = MASK_CLEAR_LAND;
            conf_mask[row][col] = CLOUD_CONFIDENCE_NONE;
        }

    /* Build the potential cloud, shadow, snow, water mask */
    status = potential_cloud_shadow_snow_mask (input, cloud_prob, &clear_ptm,
                                               &t_templ, &t_temph, pixel_mask,
                                               conf_mask, use_l8_cirrus,
                                               verbose);
    if (status != SUCCESS)
    {
        sprintf (errstr, "processing potential_cloud_shadow_snow_mask");
        CFMASK_ERROR (errstr, "main");
    }

    printf ("Pcloud done, starting cloud/shadow match\n");

    /* Build the final cloud shadow based on geometry matching and
       combine the final cloud, shadow, snow, water masks into fmask
       the pixel_mask is a bit mask as input and a value mask as output */
    status = object_cloud_shadow_match (input, clear_ptm, t_templ, t_temph,
                                        cldpix, sdpix, max_cloud_pixels,
                                        pixel_mask, verbose);
    if (status != SUCCESS)
    {
        sprintf (errstr, "processing object_cloud_and_shadow_match");
        CFMASK_ERROR (errstr, "main");
    }

    /* Reassign solar azimuth angle for output purpose if south up north
       down scene is involved */
    if (input->meta.ul_corner.is_fill &&
        input->meta.lr_corner.is_fill &&
        (input->meta.ul_corner.lat - input->meta.lr_corner.lat) < MINSIGMA)
        input->meta.sun_az = sun_azi_temp;

    /* Open the output file */
    output = OpenOutput (&xml_metadata, input);
    if (output == NULL)
    {                           /* error message already printed */
        sprintf (errstr, "Opening output file");
        CFMASK_ERROR (errstr, "main");
    }

    if (!PutOutput (output, pixel_mask))
    {
        sprintf (errstr, "Writing output fmask files\n");
        CFMASK_ERROR (errstr, "main");
    }

    /* Close the output file */
    if (!CloseOutput (output))
    {
        sprintf (errstr, "closing output file");
        CFMASK_ERROR (errstr, "main");
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct (&output->metadata.band[0], &xml_metadata.global,
                            &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Creating ENVI header structure.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Write the ENVI header */
    strcpy (envi_file, output->metadata.band[0].file_name);
    cptr = strchr (envi_file, '.');
    if (cptr == NULL)
    {
        sprintf (errstr, "error in ENVI header filename");
        CFMASK_ERROR (errstr, "main");
    }

    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Writing ENVI header file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata (output->nband, output->metadata.band, xml_name)
        != SUCCESS)
    {
        sprintf (errstr, "Appending spectral index bands to XML file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Free the structure */
    if (!FreeOutput (output))
    {
        sprintf (errstr, "freeing output file structure");
        CFMASK_ERROR (errstr, "main");
    }

    output = OpenOutputConfidence (&xml_metadata, input);
    if (output == NULL)
    {                           /* error message already printed */
        sprintf (errstr, "Opening output file");
        CFMASK_ERROR (errstr, "main");
    }

    if (!PutOutput (output, conf_mask))
    {
        sprintf (errstr, "Writing output fmask files\n");
        CFMASK_ERROR (errstr, "main");
    }

    /* Close the output file */
    if (!CloseOutput (output))
    {
        sprintf (errstr, "closing output file");
        CFMASK_ERROR (errstr, "main");
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct (&output->metadata.band[0], &xml_metadata.global,
                            &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Creating ENVI header structure.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Write the ENVI header */
    strcpy (envi_file, output->metadata.band[0].file_name);
    cptr = strchr (envi_file, '.');
    if (cptr == NULL)
    {
        sprintf (errstr, "error in ENVI header filename");
        CFMASK_ERROR (errstr, "main");
    }

    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Writing ENVI header file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata (output->nband, output->metadata.band, xml_name)
        != SUCCESS)
    {
        sprintf (errstr, "Appending spectral index bands to XML file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Free the structure */
    if (!FreeOutput (output))
    {
        sprintf (errstr, "freeing output file structure");
        CFMASK_ERROR (errstr, "main");
    }

    /* Free the metadata structure */
    free_metadata (&xml_metadata);

    /* Free the pixel mask */
    status = free_2d_array ((void **) pixel_mask);
    if (status != SUCCESS)
    {
        sprintf (errstr, "Freeing pixel mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    status = free_2d_array ((void **) conf_mask);
    if (status != SUCCESS)
    {
        sprintf (errstr, "Freeing confidence mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    /* Close the input file and free the structure */
    CloseInput (input);
    FreeInput (input);

    free (xml_name);

    printf ("Processing complete.\n");
    time (&now);
    printf ("CFmask end_time=%s\n", ctime (&now));

    return SUCCESS;
}
Пример #2
0
int main (int argc, const char **argv) {
  Param_t *param = NULL;
  Input_t *input = NULL;
  Lut_t *lut = NULL;
  Output_t *output = NULL;
  Output_t *output_th = NULL;
  int iline, isamp,oline, ib, jb, iz, val;
  unsigned char *line_in = NULL;
  unsigned char *line_in_thz = NULL;
  unsigned char *line_out_qa = NULL;
  int16 *line_out = NULL;
  int16 *line_out_th = NULL;
  int16 *line_out_thz = NULL;
  Cal_stats_t cal_stats;
  Cal_stats6_t cal_stats6;
  int nps,nls, nps6, nls6;
  int zoomx, zoomy;
  int i,odometer_flag=0;
  char msgbuf[1024];
  char envi_file[STR_SIZE]; /* name of the output ENVI header file */
  char *cptr=NULL;          /* pointer to the file extension */
  size_t input_psize;
  int qa_band = QA_BAND_NUM;
  int nband_refl = NBAND_REFL_MAX;
  int ifill, num_zero;
  int maxth=0;
  int mss_flag=0;
  Espa_internal_meta_t xml_metadata;  /* XML metadata structure */
  Envi_header_t envi_hdr;   /* output ENVI header information */

  printf ("\nRunning lndcal ...\n");
  for (i=1; i<argc; i++)if ( !strcmp(argv[i],"-o") )odometer_flag=1;

  /* Read the parameters from the input parameter file */
  param = GetParam(argc, argv);
  if (param == (Param_t *)NULL) EXIT_ERROR("getting runtime parameters",
    "main");

  /* Validate the input metadata file */
  if (validate_xml_file (param->input_xml_file_name) != SUCCESS)
  {  /* Error messages already written */
      EXIT_ERROR("Failure validating XML file", "main");
  }

  /* Initialize the metadata structure */
  init_metadata_struct (&xml_metadata);

  /* Parse the metadata file into our internal metadata structure; also
     allocates space as needed for various pointers in the global and band
     metadata */
  if (parse_metadata (param->input_xml_file_name, &xml_metadata) != SUCCESS)
  {  /* Error messages already written */
    EXIT_ERROR("parsing XML file", "main");
  }

  /* Check to see if the gain and bias values were specified */
  if (!existRadGB (&xml_metadata))
    EXIT_ERROR("Gains and biases don't exist in XML file (TOA radiance gain "
      "and bias fields) for each band.  Make sure to utilize the latest LPGS "
      "MTL file for conversion to the ESPA internal raw binary format as the "
      "gains and biases should be in that file.", "main");
  
  /* Open input file */
  input = OpenInput (&xml_metadata);
  if (input == (Input_t *)NULL)
    EXIT_ERROR("setting up input from XML structure", "main");

  /* Get Lookup table */
  lut = GetLut(param, input->nband, input);
  if (lut == (Lut_t *)NULL) EXIT_ERROR("bad lut file", "main");

  nps6=  input->size_th.s;
  nls6=  input->size_th.l;
  nps =  input->size.s;
  nls =  input->size.l;
  zoomx= nint( (float)nps / (float)nps6 );
  zoomy= nint( (float)nls / (float)nls6 );

  for (ib = 0; ib < input->nband; ib++) 
    cal_stats.first[ib] = true;
  cal_stats6.first = true;
  if (input->meta.inst == INST_MSS)mss_flag=1; 

  /* Open the output files.  Raw binary band files will be be opened. */
  output = OpenOutput(&xml_metadata, input, param, lut, false /*not thermal*/,
    mss_flag);
  if (output == NULL) EXIT_ERROR("opening output file", "main");

  /* Allocate memory for the input buffer, enough for all reflectance bands */
  input_psize = sizeof(unsigned char);
  line_in = calloc (input->size.s * nband_refl, input_psize);
   if (line_in == NULL) 
     EXIT_ERROR("allocating input line buffer", "main");

  /* Create and open output thermal band, if one exists */
  if ( input->nband_th > 0 ) {
    output_th = OpenOutput (&xml_metadata, input, param, lut, true /*thermal*/,
      mss_flag);
    if (output_th == NULL)
      EXIT_ERROR("opening output therm file", "main");

    /* Allocate memory for the thermal input and output buffer, only holds
       one band */
    line_out_th = calloc(input->size_th.s, sizeof(int16));
    if (line_out_th == NULL) 
      EXIT_ERROR("allocating thermal output line buffer", "main");

    if (zoomx == 1) {
      line_out_thz = line_out_th;
      line_in_thz = line_in;
    }
    else {
      line_out_thz = calloc (input->size.s, sizeof(int16));
      if (line_out_thz == NULL) 
        EXIT_ERROR("allocating thermal zoom output line buffer", "main");
      line_in_thz = calloc (input->size.s, input_psize);
      if (line_in_thz == NULL) 
        EXIT_ERROR("allocating thermal zoom input line buffer", "main");
    }
  } else {
    printf("*** no output thermal file ***\n"); 
  }

  /* Allocate memory for output lines for both the image and QA data */
  line_out = calloc (input->size.s, sizeof (int16));
  if (line_out == NULL) 
    EXIT_ERROR("allocating output line buffer", "main");

  line_out_qa = calloc (input->size.s, sizeof(unsigned char));
  if (line_out_qa == NULL) 
    EXIT_ERROR("allocating qa output line buffer", "main");
  memset (line_out_qa, 0, input->size.s * sizeof(unsigned char));    

  /* Do for each THERMAL line */
  oline= 0;
  if (input->nband_th > 0) {
    ifill= (int)lut->in_fill;
    for (iline = 0; iline < input->size_th.l; iline++) {
      ib=0;
      if (!GetInputLineTh(input, iline, line_in))
        EXIT_ERROR("reading input data for a line", "main");

      if ( odometer_flag && ( iline==0 || iline ==(nls-1) || iline%100==0  ) ){ 
        if ( zoomy == 1 )
          printf("--- main loop BAND6 Line %d --- \r",iline); 
        else
          printf("--- main loop BAND6 Line in=%d out=%d --- \r",iline,oline); 
        fflush(stdout); 
      }

      memset(line_out_qa, 0, input->size.s*sizeof(unsigned char));    
      if (!Cal6(lut, input, line_in, line_out_th, line_out_qa, &cal_stats6,
        iline))
        EXIT_ERROR("doing calibration for a line", "main");

      if ( zoomx>1 ) {
        zoomIt(line_out_thz, line_out_th, nps/zoomx, zoomx );
        zoomIt8(line_in_thz, line_in, nps/zoomx, zoomx );
      }

      for ( iz=0; iz<zoomy; iz++ ) {
        for (isamp = 0; isamp < input->size.s; isamp++) {
          val= getValue(line_in_thz, isamp);
          if ( val> maxth) maxth=val;
          if ( val==ifill) line_out_qa[isamp] = lut->qa_fill; 
          else if ( val>=SATU_VAL6 ) line_out_qa[isamp] = ( 0x000001 << 6 ); 
        }

        if ( oline<nls ) {
          if (!PutOutputLine(output_th, ib, oline, line_out_thz)) {
            sprintf(msgbuf,"write thermal error ib=%d oline=%d iline=%d",ib,
              oline,iline);
            EXIT_ERROR(msgbuf, "main");
          }

          if (input->meta.inst != INST_MSS) 
            if (!PutOutputLine(output_th, ib+1, oline, line_out_qa)) {
	          sprintf(msgbuf,"write thermal QA error ib=%d oline=%d iline=%d",
                ib+1,oline,iline);
              EXIT_ERROR(msgbuf, "main");
            }
        }
        oline++;
      }
    } /* end loop for each thermal line */
  }
  if (odometer_flag) printf("\n");

  if (input->nband_th > 0)
    if (!CloseOutput(output_th))
      EXIT_ERROR("closing output thermal file", "main");

  /* Do for each REFLECTIVE line */
  ifill= (int)lut->in_fill;
  for (iline = 0; iline < input->size.l; iline++){
    /* Do for each band */

    if ( odometer_flag && ( iline==0 || iline ==(nls-1) || iline%100==0  ) )
     {printf("--- main reflective loop Line %d ---\r",iline); fflush(stdout);}

    memset(line_out_qa, 0, input->size.s*sizeof(unsigned char));
    
    for (ib = 0; ib < input->nband; ib++) {
      if (!GetInputLine(input, ib, iline, &line_in[ib*nps]))
        EXIT_ERROR("reading input data for a line", "main");
    }
    
    for (isamp = 0; isamp < input->size.s; isamp++){
      num_zero=0;
      for (ib = 0; ib < input->nband; ib++) {
        jb= (ib != 5 ) ? ib+1 : ib+2;
        val= getValue((unsigned char *)&line_in[ib*nps], isamp);
	    if ( val==ifill   )num_zero++;
        if ( val==SATU_VAL[ib] ) line_out_qa[isamp]|= ( 0x000001 <<jb ); 
      }
      /* Feng fixed bug by changing "|=" to "=" below (4/17/09) */
      if ( num_zero >  0 )line_out_qa[isamp] = lut->qa_fill; 
    }

    for (ib = 0; ib < input->nband; ib++) {
      if (!Cal(lut, ib, input, &line_in[ib*nps], line_out, line_out_qa,
        &cal_stats,iline))
        EXIT_ERROR("doing calibraton for a line", "main");

      if (!PutOutputLine(output, ib, iline, line_out))
        EXIT_ERROR("reading input data for a line", "main");
    } /* End loop for each band */
        
    if (input->meta.inst != INST_MSS) 
      if (!PutOutputLine(output, qa_band, iline, line_out_qa))
        EXIT_ERROR("writing qa data for a line", "main");
  } /* End loop for each line */

  if ( odometer_flag )printf("\n");

  for (ib = 0; ib < input->nband; ib++) {
    printf(
      " band %d rad min %8.5g max %8.4f  |  ref min  %8.5f max  %8.4f\n", 
      input->meta.iband[ib], cal_stats.rad_min[ib], cal_stats.rad_max[ib],
      cal_stats.ref_min[ib], cal_stats.ref_max[ib]);
  }

  if ( input->nband_th > 0 )
    printf(
      " band %d rad min %8.5g max %8.4f  |  tmp min  %8.5f max  %8.4f\n", 6,
      cal_stats6.rad_min,  cal_stats6.rad_max,
      cal_stats6.temp_min, cal_stats6.temp_max);

  /* Close input and output files */
  if (!CloseInput(input)) EXIT_ERROR("closing input file", "main");
  if (!CloseOutput(output)) EXIT_ERROR("closing input file", "main");

  /* Write the ENVI header for reflectance files */
  for (ib = 0; ib < output->nband; ib++) {
    /* Create the ENVI header file this band */
    if (create_envi_struct (&output->metadata.band[ib], &xml_metadata.global,
      &envi_hdr) != SUCCESS)
        EXIT_ERROR("Creating the ENVI header structure for this file.", "main");

    /* Write the ENVI header */
    strcpy (envi_file, output->metadata.band[ib].file_name);
    cptr = strchr (envi_file, '.');
    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
        EXIT_ERROR("Writing the ENVI header file.", "main");
  }

  /* Write the ENVI header for thermal files */
  for (ib = 0; ib < output_th->nband; ib++) {
    /* Create the ENVI header file this band */
    if (create_envi_struct (&output_th->metadata.band[ib], &xml_metadata.global,
      &envi_hdr) != SUCCESS)
        EXIT_ERROR("Creating the ENVI header structure for this file.", "main");

    /* Write the ENVI header */
    strcpy (envi_file, output_th->metadata.band[ib].file_name);
    cptr = strchr (envi_file, '.');
    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
        EXIT_ERROR("Writing the ENVI header file.", "main");
  }

  /* Append the reflective and thermal bands to the XML file */
  if (append_metadata (output->nband, output->metadata.band,
    param->input_xml_file_name) != SUCCESS)
    EXIT_ERROR("appending reflectance and QA bands", "main");
  if (input->nband_th > 0) {
    if (append_metadata (output_th->nband, output_th->metadata.band,
      param->input_xml_file_name) != SUCCESS)
      EXIT_ERROR("appending thermal and QA bands", "main");
  }

  /* Free the metadata structure */
  free_metadata (&xml_metadata);

  /* Free memory */
  if (!FreeParam(param)) 
    EXIT_ERROR("freeing parameter stucture", "main");

  if (!FreeInput(input)) 
    EXIT_ERROR("freeing input file stucture", "main");

  if (!FreeLut(lut)) 
    EXIT_ERROR("freeing lut file stucture", "main");

  if (!FreeOutput(output)) 
    EXIT_ERROR("freeing output file stucture", "main");

  free(line_out);
  line_out = NULL;
  free(line_in);
  line_in = NULL;
  free(line_out_qa);
  line_out_qa = NULL;
  free(line_out_th);
  line_out_th = NULL;
  if (zoomx != 1) {
    free(line_in_thz);
    free(line_out_thz);
  }
  line_in_thz = NULL;
  line_out_thz = NULL;

  /* All done */
  printf ("lndcal complete.\n");
  return (EXIT_SUCCESS);
}
Пример #3
0
/******************************************************************************
METHOD:  lst

PURPOSE:  The main routine for scene based LST (Land Surface Temperature).

RETURN VALUE:
Type = int
Value           Description
-----           -----------
ERROR           An error occurred during processing of the scene_based_lst
SUCCESS         Processing was successful

PROJECT:  Land Satellites Data System Science Research and Development (LSRD)
          at the USGS EROS
******************************************************************************/
int
main (int argc, char *argv[])
{
    char FUNC_NAME[] = "main";

    Espa_internal_meta_t xml_metadata;  /* XML metadata structure */

    char msg_str[MAX_STR_LEN];
    char xml_filename[PATH_MAX];        /* input XML filename */
    char dem_filename[PATH_MAX];        /* input DEM filename */
    char emissivity_filename[PATH_MAX]; /* input Emissivity filename */
    char command[PATH_MAX];

    Input_t *input = NULL;          /* input data and meta data */
    //    Output_t *output = NULL; /* output structure and metadata */

    bool use_tape6;             /* Use the tape6 output */
    bool verbose;               /* verbose flag for printing messages */
    bool debug;                 /* debug flag for debug output */

    int modtran_run;

    double alb = 0.1;
    double **modtran_results = NULL;

    char *tmp_env = NULL;

    REANALYSIS_POINTS points;

    time_t now;

    /* Display the starting time of the application */
    time (&now);
    snprintf (msg_str, sizeof(msg_str),
              "LST start_time [%s]", ctime (&now));
    LOG_MESSAGE (msg_str, FUNC_NAME);

    /* Read the command-line arguments, including the name of the input
       Landsat TOA reflectance product and the DEM */
    if (get_args (argc, argv, xml_filename, dem_filename, emissivity_filename,
                  &use_tape6, &verbose, &debug) != SUCCESS)
    {
        RETURN_ERROR ("calling get_args", FUNC_NAME, EXIT_FAILURE);
    }

    /* Verify the existence of required environment variables */
    /* Grab the environment path to the LST_DATA_DIR */
    tmp_env = getenv ("LST_DATA_DIR");
    if (tmp_env == NULL)
    {
        RETURN_ERROR ("LST_DATA_DIR environment variable is not set",
                      FUNC_NAME, EXIT_FAILURE);
    }

    /* Validate the input metadata file */
    if (validate_xml_file (xml_filename) != SUCCESS)
    {
        /* Error messages already written */
        return EXIT_FAILURE;
    }

    /* Initialize the metadata structure */
    init_metadata_struct (&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata (xml_filename, &xml_metadata) != SUCCESS)
    {
        /* Error messages already written */
        return EXIT_FAILURE;
    }

    /* Open input file, read metadata, and set up buffers */
    input = OpenInput (&xml_metadata);
    if (input == NULL)
    {
        RETURN_ERROR ("opening input files", FUNC_NAME, EXIT_FAILURE);
    }

    if (verbose)
    {
        /* Print some info to show how the input metadata works */
        printf ("Satellite: %d\n", input->meta.satellite);
        printf ("Instrument: %d\n", input->meta.instrument);

        printf ("Number of input lines: %d\n", input->thermal.size.l);
        printf ("Number of input samples: %d\n", input->thermal.size.s);

        printf ("Fill value is %d\n", input->thermal.fill_value);

        printf ("Thermal Band -->\n");
        printf ("  therm_gain: %f\n  therm_bias: %f\n",
                input->thermal.rad_gain, input->thermal.rad_bias);

        printf ("Year, Month, Day, Hour, Minute, Second:"
                " %d, %d, %d, %d, %d, %f\n",
                input->meta.acq_date.year, input->meta.acq_date.month,
                input->meta.acq_date.day, input->meta.acq_date.hour,
                input->meta.acq_date.minute, input->meta.acq_date.second);
        printf ("ACQUISITION_DATE.DOY is %d\n",
                input->meta.acq_date.doy);

        printf ("UL_MAP_CORNER: %f, %f\n", input->meta.ul_map_corner.x,
                input->meta.ul_map_corner.y);
        printf ("LR_MAP_CORNER: %f, %f\n", input->meta.lr_map_corner.x,
                input->meta.lr_map_corner.y);
        printf ("UL_GEO_CORNER: %f, %f\n",
                input->meta.ul_geo_corner.lat, input->meta.ul_geo_corner.lon);
        printf ("LR_GEO_CORNER: %f, %f\n",
                input->meta.lr_geo_corner.lat, input->meta.lr_geo_corner.lon);
    }

    /* Build the points that will be used */
    if (build_points (input, &points) != SUCCESS)
    {
        RETURN_ERROR ("Building POINTS input\n", FUNC_NAME, EXIT_FAILURE);
    }

    if (verbose)
    {
        printf ("Number of Points: %d\n", points.num_points);
    }

    /* Call build_modtran_input to generate the tape5 file input and
       the MODTRAN commands for each point and height */
    if (build_modtran_input (input, &points, verbose, debug)
        != SUCCESS)
    {
        RETURN_ERROR ("Building MODTRAN input\n", FUNC_NAME, EXIT_FAILURE);
    }

    /* Perform MODTRAN runs by calling each command */
    for (modtran_run = 0; modtran_run < points.num_modtran_runs; modtran_run++)
    {
        snprintf (msg_str, sizeof(msg_str),
                  "Executing MODTRAN [%s]",
                   points.modtran_runs[modtran_run].command);
        LOG_MESSAGE (msg_str, FUNC_NAME);

#if RUN_MODTRAN
        if (system (points.modtran_runs[modtran_run].command) != SUCCESS)
        {
            RETURN_ERROR ("Error executing MODTRAN", FUNC_NAME,
                          EXIT_FAILURE);
        }
#endif
    }

    /* PARSING MODTRAN RESULTS:
       for each case in caseList (for each modtran run),
       parse wavelength and total radiance from tape6 file into parsed */
    for (modtran_run = 0; modtran_run < points.num_modtran_runs; modtran_run++)
    {
        if (use_tape6)
        {
            /* Use modtran generated tape6 output */
            snprintf (command, sizeof (command),
                      "lst_extract_modtran_results.py"
                      " --tape6"
                      " --input-path %s"
                      " --output-path %s",
                      points.modtran_runs[modtran_run].path,
                      points.modtran_runs[modtran_run].path);
        }
        else
        {
            /* Use modtran generated pltout.asc output */
            snprintf (command, sizeof (command),
                      "lst_extract_modtran_results.py"
                      " --pltout"
                      " --input-path %s"
                      " --output-path %s",
                      points.modtran_runs[modtran_run].path,
                      points.modtran_runs[modtran_run].path);
        }

        snprintf (msg_str, sizeof(msg_str), "Executing [%s]", command);
        LOG_MESSAGE (msg_str, FUNC_NAME);

#if EXTRACT_TAPE6_RESULTS
        if (system (command) != SUCCESS)
        {
            RETURN_ERROR ("Failed executing lst_extract_tape6_results.py",
                          FUNC_NAME, EXIT_FAILURE);
        }
#endif
    }

    /* Allocate memory for MODTRAN results */
    modtran_results =
        (double **) allocate_2d_array (points.num_points * NUM_ELEVATIONS,
                                       MGPE_NUM_ELEMENTS, sizeof (double));
    if (modtran_results == NULL)
    {
        RETURN_ERROR ("Allocating MODTRAN results memory", FUNC_NAME,
                      EXIT_FAILURE);
    }

    /* Generate parameters for each height and NARR point */
    if (calculate_point_atmospheric_parameters (input, &points, alb,
                                                modtran_results, verbose)
        != SUCCESS)
    {
        RETURN_ERROR ("Calculating point atmospheric parameters\n",
                      FUNC_NAME, EXIT_FAILURE);
    }

    /* Generate parameters for each Landsat pixel */
    if (calculate_pixel_atmospheric_parameters (input, &points,
                                                xml_filename,
                                                dem_filename,
                                                emissivity_filename,
                                                modtran_results, verbose)
        != SUCCESS)
    {
        RETURN_ERROR ("Calculating per/pixel atmospheric parameters\n",
                      FUNC_NAME, EXIT_FAILURE);
    }

    /* Free memory allocation */
    free_points_memory (&points);

#if NOT_TESTED
    /* Open the output file */
    output = OpenOutput (&xml_metadata, input);
    if (output == NULL)
    {                           /* error message already printed */
        RETURN_ERROR ("Opening output file", FUNC_NAME, EXIT_FAILURE);
    }

    if (!PutOutput (output, pixel_mask))
    {
        RETURN_ERROR ("Writing output LST in HDF files\n", FUNC_NAME,
                      EXIT_FAILURE);
    }

    /* Close the output file */
    if (!CloseOutput (output))
    {
        RETURN_ERROR ("closing output file", FUNC_NAME, EXIT_FAILURE);
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct (&output->metadata.band[0], &xml_metadata.global,
                            &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR ("Creating ENVI header structure.", FUNC_NAME,
                      EXIT_FAILURE);
    }

    /* Write the ENVI header */
    strcpy (envi_file, output->metadata.band[0].file_name);
    cptr = strchr (envi_file, '.');
    if (cptr == NULL)
    {
        RETURN_ERROR ("error in ENVI header filename", FUNC_NAME,
                      EXIT_FAILURE);
    }

    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR ("Writing ENVI header file.", FUNC_NAME, EXIT_FAILURE);
    }

    /* Append the LST band to the XML file */
    if (append_metadata (output->nband, output->metadata.band, xml_filename)
        != SUCCESS)
    {
        RETURN_ERROR ("Appending spectral index bands to XML file.",
                      FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the structure */
    if (!FreeOutput (output))
    {
        RETURN_ERROR ("freeing output file structure", FUNC_NAME,
                      EXIT_FAILURE);
    }
#endif

    /* Free the metadata structure */
    free_metadata (&xml_metadata);

    /* Close the input file and free the structure */
    CloseInput (input);
    FreeInput (input);

    /* Free memory allocations */
    if (free_2d_array ((void **) modtran_results) != SUCCESS)
    {
        RETURN_ERROR ("Freeing memory: MODTRAN results\n", FUNC_NAME,
                      EXIT_FAILURE);
    }

    if (!debug)
    {
        /* Delete temporary file */
        if (unlink ("atmospheric_parameters.txt") != SUCCESS)
        {
            RETURN_ERROR ("Deleting atmospheric_parameters.txt files\n",
                          FUNC_NAME, EXIT_FAILURE);
        }

        if (unlink ("base_head.txt") != SUCCESS)
        {
            RETURN_ERROR ("Deleting baseHead.txt files\n", FUNC_NAME,
                          EXIT_FAILURE);
        }

        if (unlink ("new_tail.txt") != SUCCESS)
        {
            RETURN_ERROR ("Deleting newTail.txt files\n", FUNC_NAME,
                          EXIT_FAILURE);
        }

        if (unlink ("temp_layers.txt") != SUCCESS)
        {
            RETURN_ERROR ("Deleting tempLayers.txt file\n", FUNC_NAME,
                          EXIT_FAILURE);
        }

        if (unlink ("used_points.txt") != SUCCESS)
        {
            RETURN_ERROR ("Deleting used_points.txt file\n", FUNC_NAME,
                          EXIT_FAILURE);
        }
    }

    time (&now);
    snprintf (msg_str, sizeof(msg_str),
              "scene_based_lst end_time=%s\n", ctime (&now));
    LOG_MESSAGE (msg_str, FUNC_NAME);

    return EXIT_SUCCESS;
}
Пример #4
0
/*****************************************************************************
METHOD:  cfmask

PURPOSE:  The main routine for fmask written in C

RETURN VALUE: Type = int
    Value           Description
    -----           -----------
    ERROR           An error occurred during processing of cfmask
    SUCCESS         Processing was successful
*****************************************************************************/
int
main (int argc, char *argv[])
{
    char *FUNC_NAME = "main";
    char *ext = NULL;            /* pointer to the file extension */
    char *xml_name = NULL;       /* input XML filename */
    char envi_file[MAX_STR_LEN]; /* output ENVI file name */
    char temp_file[MAX_STR_LEN]; /* temp file name */

    int status;
    int band_index;

    bool verbose;            /* verbose flag for printing messages */
    bool use_cirrus;         /* should we use Cirrus during determination? */
    bool use_thermal;        /* should we use Thermal during determination? */

    Input_t *input = NULL;    /* input data and meta data */
    Output_t *output = NULL;  /* output structure and metadata */
    Espa_internal_meta_t xml_metadata; /* XML metadata structure */
    Envi_header_t envi_hdr;            /* output ENVI header information */

    unsigned char *pixel_mask = NULL; /* pixel mask */
    unsigned char *conf_mask = NULL;  /* confidence mask */

    float clear_ptm;          /* percent of clear-sky pixels */
    float t_templ = 0.0;      /* percentile of low background temperature */
    float t_temph = 0.0;      /* percentile of high background temperature */

    int cldpix = 2;           /* Default buffer for cloud pixel dilate */
    int sdpix = 2;            /* Default buffer for shadow pixel dilate */
    float cloud_prob;         /* Default cloud probability */
    float sun_azi_temp = 0.0; /* Keep the original sun azimuth angle */

    int pixel_count;
    int pixel_index;

    time_t now;
    time(&now);

    /* Read the command-line arguments, including the name of the input
       Landsat TOA reflectance product and the DEM */
    status = get_args(argc, argv, &xml_name, &cloud_prob, &cldpix,
                      &sdpix, &use_cirrus, &use_thermal, &verbose);
    if (status != SUCCESS)
    {
        RETURN_ERROR("calling get_args", FUNC_NAME, EXIT_FAILURE);
    }

    printf("CFmask start_time=%s\n", ctime(&now));

    /* Validate the input metadata file */
    if (validate_xml_file(xml_name) != SUCCESS)
    {
        RETURN_ERROR("XML validation error", FUNC_NAME, EXIT_FAILURE);
    }

    /* Initialize the metadata structure */
    init_metadata_struct(&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata(xml_name, &xml_metadata) != SUCCESS)
    {
        RETURN_ERROR("XML parsing error", FUNC_NAME, EXIT_FAILURE);
    }

    /* Open input file, read metadata, and set up buffers */
    input = OpenInput(&xml_metadata, use_thermal);
    if (input == NULL)
    {
        RETURN_ERROR("opening input data specified in input XML",
                     FUNC_NAME, EXIT_FAILURE);
    }

    if (verbose)
    {
        /* Print some info to show how the input metadata works */
        printf("Number of input TOA bands: %d\n", input->num_toa_bands);
        printf("Number of input thermal bands: %d\n", 1);
        printf("Number of input TOA lines: %d\n", input->size.l);
        printf("Number of input TOA samples: %d\n", input->size.s);
        printf("Fill value is %d\n", input->meta.fill);
        for (band_index = 0; band_index < MAX_BAND_COUNT; band_index++)
        {
            printf("Band %d-->\n", band_index);
            if (input->satellite != IS_LANDSAT_8)
            {
                /* Landsat 8 doesn't have saturation issues */
                printf("  band satu_value_ref: %d\n",
                       input->meta.satu_value_ref[band_index]);
                printf("  band satu_value_max: %d\n",
                       input->meta.satu_value_max[band_index]);
            }
            printf("  band gain: %f, band bias: %f\n",
                   input->meta.gain[band_index], input->meta.bias[band_index]);
        }

        printf("SUN AZIMUTH is %f\n", input->meta.sun_az);
        printf("SUN ZENITH is %f\n", input->meta.sun_zen);
    }

    /* If the scene is an ascending polar scene (flipped upside down), then
       the solar azimuth needs to be adjusted by 180 degrees.  The scene in
       this case would be north down and the solar azimuth is based on north
       being up clock-wise direction. Flip the south to be up will not change
       the actual sun location, with the below relations, the solar azimuth
       angle will need add in 180.0 for correct sun location */
    if (input->meta.ul_corner.lat < input->meta.lr_corner.lat)
    {
        /* Keep the original solar azimuth angle */
        sun_azi_temp = input->meta.sun_az;
        input->meta.sun_az += 180.0;
        if (input->meta.sun_az > 360.0)
        {
            input->meta.sun_az -= 360.0;
        }
        if (verbose)
        {
            printf("Polar or ascending scene."
                    "  Readjusting solar azimuth by 180 degrees.\n"
                    "  New value: %f degrees\n", input->meta.sun_az);
        }
    }

    pixel_count = input->size.l * input->size.s;

    /* Dynamic allocate the 2d mask memory */
    pixel_mask = calloc(pixel_count, sizeof(unsigned char));
    if (pixel_mask == NULL)
    {
        RETURN_ERROR("Allocating pixel mask memory", FUNC_NAME, EXIT_FAILURE);
    }

    conf_mask = calloc(pixel_count, sizeof(unsigned char));
    if (conf_mask == NULL)
    {
        RETURN_ERROR("Allocating confidence mask memory",
                     FUNC_NAME, EXIT_FAILURE);
    }

    /* Initialize the mask to clear data */
    for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
    {
        pixel_mask[pixel_index] = CF_NO_BITS;
        conf_mask[pixel_index] = CLOUD_CONFIDENCE_NONE;
    }

    /* Build the potential cloud, shadow, snow, water mask */
    status = potential_cloud_shadow_snow_mask(input, cloud_prob, &clear_ptm,
                                              &t_templ, &t_temph, pixel_mask,
                                              conf_mask, use_cirrus,
                                              use_thermal, verbose);
    if (status != SUCCESS)
    {
        RETURN_ERROR("processing potential_cloud_shadow_snow_mask",
                     FUNC_NAME, EXIT_FAILURE);
    }
    printf("Potential Cloud Shadow: Done\n");

    /* Build the final cloud shadow based on geometry matching and
       combine the final cloud, shadow, snow, water masks into fmask
       the pixel_mask is a bit mask as input and a value mask as output */
    int data_count = 0;
    status = object_cloud_shadow_match(input, clear_ptm, t_templ, t_temph,
                                       cldpix, sdpix, pixel_mask, &data_count,
                                       use_thermal, verbose);
    if (status != SUCCESS)
    {
        RETURN_ERROR("processing object_cloud_and_shadow_match",
                     FUNC_NAME, EXIT_FAILURE);
    }
    printf("Object Cloud Shadow Matching: Done\n");

    /* Convert the pixel_mask to a value mask
       Also retrieve and report statistics */
    float clear_percent = 0; /* Percent of clear pixels in the image data */
    float cloud_percent = 0; /* Percent of cloud pixels in the image data */
    float cloud_shadow_percent = 0; /* Percent of cloud shadow pixels in the
                                       image data */
    float water_percent = 0; /* Percent of water pixels in the image data */
    float snow_percent = 0;  /* Percent of snow pixels in the image data */
    convert_and_generate_statistics(verbose, pixel_mask,
                                    input->size.l * input->size.s,
                                    data_count, &clear_percent,
                                    &cloud_percent, &cloud_shadow_percent,
                                    &water_percent, &snow_percent);
    printf("Statistics Generation: Done\n");

    /* Reassign solar azimuth angle for output purpose if south up north
       down scene is involved */
    if (input->meta.ul_corner.lat < input->meta.lr_corner.lat)
    {
        input->meta.sun_az = sun_azi_temp;
    }

    /* Open the output file */
    output = OpenOutputCFmask(&xml_metadata, input, clear_percent,
                              cloud_percent, cloud_shadow_percent,
                              water_percent, snow_percent);
    if (output == NULL)
    {
        RETURN_ERROR("Opening output file", FUNC_NAME, EXIT_FAILURE);
    }

    if (!PutOutput(output, pixel_mask))
    {
        RETURN_ERROR("Writing output fmask files", FUNC_NAME, EXIT_FAILURE);
    }

    /* Close the output file */
    if (!CloseOutput(output))
    {
        RETURN_ERROR("closing output file", FUNC_NAME, EXIT_FAILURE);
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct(&output->metadata.band[0], &xml_metadata.global,
                           &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Creating ENVI header structure.", FUNC_NAME,
                     EXIT_FAILURE);
    }

    /* Write the ENVI header */
    snprintf(temp_file, sizeof(temp_file), "%s",
             output->metadata.band[0].file_name);
    ext = strrchr(temp_file, '.');
    if (ext == NULL)
    {
        RETURN_ERROR("error in ENVI header filename", FUNC_NAME, EXIT_FAILURE);
    }

    ext[0] = '\0';
    snprintf(envi_file, sizeof(envi_file), "%s.hdr", temp_file);
    if (write_envi_hdr(envi_file, &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Writing ENVI header file.", FUNC_NAME, EXIT_FAILURE);
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata(output->nband, output->metadata.band, xml_name)
        != SUCCESS)
    {
        RETURN_ERROR("Appending spectral index bands to XML file.",
                     FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the structure */
    if (!FreeOutput(output))
    {
        RETURN_ERROR("freeing output file structure", FUNC_NAME, EXIT_FAILURE);
    }

    output = OpenOutputConfidence(&xml_metadata, input);
    if (output == NULL)
    {
        RETURN_ERROR("Opening output file", FUNC_NAME, EXIT_FAILURE);
    }

    if (!PutOutput(output, conf_mask))
    {
        RETURN_ERROR("Writing output fmask files", FUNC_NAME, EXIT_FAILURE);
    }

    /* Close the output file */
    if (!CloseOutput(output))
    {
        RETURN_ERROR("closing output file", FUNC_NAME, EXIT_FAILURE);
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct(&output->metadata.band[0], &xml_metadata.global,
                           &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Creating ENVI header structure.", FUNC_NAME,
                     EXIT_FAILURE);
    }

    /* Write the ENVI header */
    snprintf(temp_file, sizeof(temp_file), "%s",
             output->metadata.band[0].file_name);
    ext = strrchr(temp_file, '.');
    if (ext == NULL)
    {
        RETURN_ERROR("error in ENVI header filename", FUNC_NAME, EXIT_FAILURE);
    }

    ext[0] = '\0';
    snprintf(envi_file, sizeof(envi_file), "%s.hdr", temp_file);
    if (write_envi_hdr(envi_file, &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Writing ENVI header file.", FUNC_NAME, EXIT_FAILURE);
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata(output->nband, output->metadata.band,
                        xml_name) != SUCCESS)
    {
        RETURN_ERROR("Appending spectral index bands to XML file.",
                     FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the structure */
    if (!FreeOutput(output))
    {
        RETURN_ERROR("freeing output file structure", FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the metadata structure */
    free_metadata(&xml_metadata);

    /* Free the pixel mask */
    free(pixel_mask);
    pixel_mask = NULL;
    free(conf_mask);
    conf_mask = NULL;

    /* Close the input file and free the structure */
    CloseInput(input);
    FreeInput(input);

    free(xml_name);
    xml_name = NULL;

    printf("Processing complete.\n");
    time(&now);
    printf("CFmask end_time=%s\n", ctime(&now));

    return SUCCESS;
}
/******************************************************************************
MODULE:  main

PURPOSE: Creates the Landsat solar and view/satellite per-pixel angles.  Both
the zenith and azimuth angles are created for each angle type for the
representative band.

RETURN VALUE:
Type = int
Value           Description
-----           -----------
ERROR           Error creating the angle bands
SUCCESS         No errors encountered

NOTES:
1. Angles are written in degrees and scaled by 100.
2. There are 4 bands written for the representative band: solar zenith, solar
   azimuth, sensor zenith, sensor azimuth.
3. The landsat_per_pixel_angles library expects an array of solar/satellite
   azimuth/zenith pointers for all the input bands, as that's the possible
   full list of output bands.  So, even though we are not processing all the
   bands in the output list, we still need to provide an array of that size.
   This requires us to keep track of the indices for each of the output bands
   into the array of input bands, for both ETM and TM arrays.
******************************************************************************/
int main (int argc, char** argv)
{
    char FUNC_NAME[] = "create_angle_bands";  /* function name */
    char errmsg[STR_SIZE];       /* error message */
    char tmpstr[STR_SIZE];       /* temporary string */
    char tmpfile[STR_SIZE];      /* temporary filename */
    char ang_infile[STR_SIZE];   /* input angle coefficient filename */
    char outfile[STR_SIZE];      /* output base filename for angle bands */
    char band_list[STR_SIZE];    /* char array of list of bands to process */
    char etm_list[] = "4";       /* list of ETM bands to process */
    char tm_list[] = "4";        /* list of TM bands to process */
    char production_date[MAX_DATE_LEN+1]; /* current date/year for production */
    char band_angle[NANGLE_BANDS][STR_SIZE] = {"solar zenith", "solar azimuth",
                                    "sensor zenith", "sensor azimuth"};
    char *cptr = NULL;           /* pointer to file extension */
    char *xml_infile = NULL;     /* input XML filename */
    bool process_l7 = false;     /* are we processing L7 vs. L4-5 */
    bool process_l45 = false;    /* are we processing L4-5 vs. L7 */
    int i;                       /* looping variable for bands */
    int curr_bnd;                /* current input band location */
    int curr_band;               /* current input band number */
    int curr_bndx;               /* index of current input band */
    int curr_index;              /* index of current output band in the input
                                    band array */
    int etm_nbands = 1;          /* number of ETM bands to process for PPA */
    int tm_nbands = 1;           /* number of TM bands to process for PPA */
    int landsat_nbands;          /* number of bands to process for PPA */
    int *landsat_bands = NULL;   /* array of output bands to be processed;
                                    set to point to either etm_bands or
                                    tm_bands */
    int etm_bands[] = {4};       /* output bands to be processed */
    int tm_bands[] = {4};        /* output bands to be processed */
    int *band_indx = NULL;       /* array of indices for the output bands within
                                    the full set of input bands; set to point
                                    to either etm_band_indx or tm_band_indx */
    int etm_band_indx[] = {3};   /* index in the overall input bands for
                                    the output bands [1,2,3,4,5,61,62,7,8] */
    int tm_band_indx[] = {3};    /* index in the overall input bands for
                                    the output bands [1,2,3,4,5,6,7,8] */
    int out_nbands;              /* number of output bands to be written */
    int nlines[L7_NBANDS];       /* number of lines for each band */
    int nsamps[L7_NBANDS];       /* number of samples for each band */
    Angle_band_t ang;            /* looping variable for solar/senor angle */
    short *solar_zenith[L7_NBANDS];  /* array of pointers for the solar zenith
                                        angle array, one per band */
    short *solar_azimuth[L7_NBANDS]; /* array of pointers for the solar azimuth
                                        angle array, one per band */
    short *sat_zenith[L7_NBANDS];    /* array of pointers for the satellite
                                        zenith angle array, one per band */
    short *sat_azimuth[L7_NBANDS];   /* array of pointers for the satellite
                                        azimuth angle array, one per band */
    short *curr_angle = NULL;      /* pointer to the current angle array */
    time_t tp;                     /* time structure */
    struct tm *tm = NULL;          /* time structure for UTC time */
    FILE *fptr=NULL;               /* file pointer */
    Envi_header_t envi_hdr;        /* output ENVI header information */
    Espa_internal_meta_t xml_metadata;
                                   /* XML metadata structure to be populated by
                                      reading the input XML metadata file */
    Espa_band_meta_t *bmeta=NULL;    /* pointer to array of bands metadata */
    Espa_global_meta_t *gmeta=NULL;  /* pointer to the global metadata struct */
    Espa_band_meta_t *out_bmeta = NULL; /* band metadata for angle bands */
    Espa_internal_meta_t out_meta;      /* output metadata for angle bands */

    /* Read the command-line arguments */
    if (get_args (argc, argv, &xml_infile) != SUCCESS)
    {   /* get_args already printed the error message */
        exit (ERROR);
    }
    printf ("Processing the per-pixel angle bands for L4-7 ...\n");

    /* Validate the input metadata file */
    if (validate_xml_file (xml_infile) != SUCCESS)
    {  /* Error messages already written */
        return (ERROR);
    }

    /* Initialize the metadata structure */
    init_metadata_struct (&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata (xml_infile, &xml_metadata) != SUCCESS)
    {  /* Error messages already written */
        return (ERROR);
    }
    bmeta = xml_metadata.band;
    gmeta = &xml_metadata.global;

    /* Determine which instrument is being processed */
    if (!strncmp (gmeta->instrument, "ETM", 3))
        process_l7 = true;
    else
        process_l45 = true;

    /* Determine the angle coefficient filename and the output file basename */
    strcpy (ang_infile, xml_infile);
    cptr = strchr (ang_infile, '.');
    strcpy (cptr, "_ANG.txt");

    strcpy (outfile, xml_infile);
    cptr = strchr (outfile, '.');
    *cptr = '\0';

    /* Initialize the output metadata structure.  The global metadata will
       not be used and will not be valid. */
    init_metadata_struct (&out_meta);

    /* Determine the number of output bands */
    if (process_l7)
    {
        landsat_bands = etm_bands;
        landsat_nbands = etm_nbands;
        out_nbands = landsat_nbands * NANGLE_BANDS;
        band_indx = etm_band_indx;
        strcpy (band_list, etm_list);
    }
    else
    {
        landsat_bands = tm_bands;
        landsat_nbands = tm_nbands;
        out_nbands = landsat_nbands * NANGLE_BANDS;
        band_indx = tm_band_indx;
        strcpy (band_list, tm_list);
    }

    /* Allocate memory for the output bands */
    if (allocate_band_metadata (&out_meta, out_nbands) != SUCCESS)
    {
        sprintf (errmsg, "Cannot allocate memory for the %d angle bands",
            out_nbands);
        error_handler (true, FUNC_NAME, errmsg);
        exit (ERROR);
    }

    /* Get the current date/time (UTC) for the production date of each band */
    if (time (&tp) == -1)
    {
        sprintf (errmsg, "Unable to obtain the current time.");
        error_handler (true, FUNC_NAME, errmsg);
        exit (ERROR);
    }

    tm = gmtime (&tp);
    if (tm == NULL)
    {
        sprintf (errmsg, "Converting time to UTC.");
        error_handler (true, FUNC_NAME, errmsg);
        exit (ERROR);
    }

    if (strftime (production_date, MAX_DATE_LEN, "%Y-%m-%dT%H:%M:%SZ", tm) == 0)
    {
        sprintf (errmsg, "Formatting the production date/time.");
        error_handler (true, FUNC_NAME, errmsg);
        exit (ERROR);
    }

    /* Initialize the Landsat angle bands to NULL */
    init_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
        sat_azimuth);

    /* Create the Landsat angle bands for the specified bands.  Create a full
       resolution product. */
    if (landsat_per_pixel_angles (ang_infile, 1, band_list, solar_zenith,
        solar_azimuth, sat_zenith, sat_azimuth, nlines, nsamps) != SUCCESS)
    {  /* Error messages already written */
        free_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
            sat_azimuth);
        exit (ERROR);
    }

    /* Setup the XML file for these bands */
    for (i = 0; i < out_nbands; i++)
    {
        /* Set up the band metadata for the current band */
        out_bmeta = &out_meta.band[i];
        strcpy (out_bmeta->product, "angle_bands");
        strcpy (out_bmeta->source, "level1");
        strcpy (out_bmeta->category, "image");

        /* Setup filename-related items for all four bands: solar zenith,
           solar azimuth, sensor zenith, sensor azimuth.  L4-5 and L8 band
           numbers follow a normal numbering scheme.  L7 band numbering
           needs a little help to get it correct. */
        curr_bnd = i / NANGLE_BANDS + 1;  /* current input band number */
        curr_bndx = curr_bnd - 1;         /* index of current input band */
        curr_band = landsat_bands[curr_bndx]; /* actual band number */
        curr_index = band_indx[curr_bndx];    /* index in output array */

        switch (i % NANGLE_BANDS)
        {
            case (SOLAR_ZEN):  /* solar zenith */
                /* Determine the output file for the solar zenith band */
                snprintf (tmpfile, sizeof (tmpfile),
                    "%s_b%d_solar_zenith.img", outfile, curr_band);
                sprintf (out_bmeta->name, "solar_zenith_band%d", curr_band);
                strncpy (tmpstr, bmeta[curr_bndx].short_name, 4);
                tmpstr[4] = '\0';
                sprintf (out_bmeta->short_name, "%sSOLZEN", tmpstr);
                sprintf (out_bmeta->long_name,
                    "band %d solar zenith angles", curr_band);
                break;

            case (SOLAR_AZ):  /* solar azimuth */
                /* Determine the output file for the solar azimuth band */
                snprintf (tmpfile, sizeof (tmpfile),
                    "%s_b%d_solar_azimuth.img", outfile, curr_band);
                sprintf (out_bmeta->name, "solar_azimuth_band%d",
                    curr_band);
                strncpy (tmpstr, bmeta[curr_bndx].short_name, 4);
                tmpstr[4] = '\0';
                sprintf (out_bmeta->short_name, "%sSOLAZ", tmpstr);
                sprintf (out_bmeta->long_name,
                    "band %d solar azimuth angles", curr_band);
                break;

            case (SENSOR_ZEN):  /* sensor zenith */
                /* Determine the output file for the sensor zenith band */
                snprintf (tmpfile, sizeof (tmpfile),
                    "%s_b%d_sensor_zenith.img", outfile, curr_band);
                sprintf (out_bmeta->name, "sensor_zenith_band%d",
                    curr_band);
                strncpy (tmpstr, bmeta[curr_bndx].short_name, 4);
                tmpstr[4] = '\0';
                sprintf (out_bmeta->short_name, "%sSENZEN", tmpstr);
                sprintf (out_bmeta->long_name,
                    "band %d sensor zenith angles", curr_band);
                break;

            case (SENSOR_AZ):  /* sensor azimuth */
                /* Determine the output file for the sensor azimuth band */
                snprintf (tmpfile, sizeof (tmpfile),
                    "%s_b%d_sensor_azimuth.img", outfile, curr_band);
                sprintf (out_bmeta->name, "sensor_azimuth_band%d",
                    curr_band);
                strncpy (tmpstr, bmeta[curr_bndx].short_name, 4);
                tmpstr[4] = '\0';
                sprintf (out_bmeta->short_name, "%sSENAZ", tmpstr);
                sprintf (out_bmeta->long_name,
                    "band %d sensor azimuth angles", curr_band);
                break;
        }

        snprintf (out_bmeta->file_name, sizeof (out_bmeta->file_name), "%s",
            tmpfile);
        out_bmeta->data_type = ESPA_INT16;
        out_bmeta->fill_value = ANGLE_BAND_FILL;
        out_bmeta->scale_factor = ANGLE_BAND_SCALE_FACT;
        strcpy (out_bmeta->data_units, "degrees");
        out_bmeta->nlines = nlines[curr_index];
        out_bmeta->nsamps = nsamps[curr_index];
        out_bmeta->pixel_size[0] = bmeta[curr_bndx].pixel_size[0];
        out_bmeta->pixel_size[1] = bmeta[curr_bndx].pixel_size[1];
        strcpy (out_bmeta->pixel_units, bmeta[curr_bndx].pixel_units);
        sprintf (out_bmeta->app_version, "create_angle_bands_%s",
            ESPA_COMMON_VERSION);
        strcpy (out_bmeta->production_date, production_date);
    }

    /* Loop through the four different angle files and write them for each
       band */
    for (ang = 0; ang < NANGLE_BANDS; ang++)
    {
        /* Write the angle bands */
        for (i = 0; i < landsat_nbands; i++)
        {
            /* Determine the index of this band in the overall list of input
               bands */
            curr_index = band_indx[i];

            /* Grab the correct data array to be written for this angle
               band */
            switch (ang)
            {
                case (SOLAR_ZEN):
                    curr_angle = &solar_zenith[curr_index][0];
                    break;
                case (SOLAR_AZ):
                    curr_angle = &solar_azimuth[curr_index][0];
                    break;
                case (SENSOR_ZEN):
                    curr_angle = &sat_zenith[curr_index][0];
                    break;
                case (SENSOR_AZ):
                    curr_angle = &sat_azimuth[curr_index][0];
                    break;
                default:
                    free_per_pixel_angles (solar_zenith, solar_azimuth,
                        sat_zenith, sat_azimuth);
                    sprintf (errmsg, "Invalid angle type %d", ang);
                    error_handler (true, FUNC_NAME, errmsg);
                    exit (ERROR);
            }

            /* Open the output file for this band */
            out_bmeta = &out_meta.band[i*NANGLE_BANDS + ang];
            fptr = open_raw_binary (out_bmeta->file_name, "wb");
            if (!fptr)
            {
                free_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
                    sat_azimuth);
                sprintf (errmsg, "Unable to open the %s file",
                    band_angle[ang]);
                error_handler (true, FUNC_NAME, errmsg);
                exit (ERROR);
            }

            /* Write the data for this band */
            if (write_raw_binary (fptr, nlines[curr_index], nsamps[curr_index],
                sizeof (short), curr_angle) != SUCCESS)
            {
                free_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
                    sat_azimuth);
                sprintf (errmsg, "Unable to write to the %s file",
                    band_angle[ang]);
                error_handler (true, FUNC_NAME, errmsg);
                exit (ERROR);
            }

            /* Close the file for this band */
            close_raw_binary (fptr);

            /* Create the ENVI header */
            if (create_envi_struct (out_bmeta, gmeta, &envi_hdr) != SUCCESS)
            {
                free_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
                    sat_azimuth);
                sprintf (errmsg, "Error creating the ENVI header file.");
                error_handler (true, FUNC_NAME, errmsg);
                exit (ERROR);
            }

            /* Write the ENVI header */
            sprintf (tmpfile, "%s", out_bmeta->file_name);
            sprintf (&tmpfile[strlen(tmpfile)-3], "hdr");
            if (write_envi_hdr (tmpfile, &envi_hdr) != SUCCESS)
            {
                free_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
                    sat_azimuth);
                sprintf (errmsg, "Writing the ENVI header file: %s.",
                    tmpfile);
                error_handler (true, FUNC_NAME, errmsg);
                exit (ERROR);
            }
        }  /* for i < landsat_nbands */
    }  /* for ang < NANGLE_BANDS */

    /* Free the pointers */
    free_per_pixel_angles (solar_zenith, solar_azimuth, sat_zenith,
        sat_azimuth);

    /* Append the solar/sensor angle bands to the XML file */
    if (append_metadata (out_nbands, out_meta.band, xml_infile) != SUCCESS)
    {
        sprintf (errmsg, "Appending solar/sensor angle bands to the XML file.");
        error_handler (true, FUNC_NAME, errmsg);
        exit (ERROR);
    }

    /* Free the input and output XML metadata */
    free_metadata (&xml_metadata);
    free_metadata (&out_meta);

    /* Free the pointers */
    free (xml_infile);

    /* Successful completion */
    exit (SUCCESS);
}