Example #1
0
meta_parameters *iso2meta(iso_meta *iso)
{
  int ii;
  meta_parameters *meta = raw_init();
  char str[30];

  // Convenience pointers
  iso_generalHeader *header = iso->generalHeader;
  iso_productComponents *comps = iso->productComponents;
  iso_productInfo *info = iso->productInfo;
  iso_productSpecific *spec = iso->productSpecific;
  iso_setup *setup = iso->setup;
  iso_processing *proc = iso->processing;
  iso_instrument *inst = iso->instrument;
  iso_platform *platform = iso->platform;
  iso_productQuality *quality = iso->productQuality;

  meta->meta_version = 3.5;

  // General block
  for (ii=0; ii<comps->numAnnotations; ii++)
    if (comps->annotation[ii].type == MAIN_TYPE)
      strncpy(meta->general->basename, comps->annotation[ii].file.name, 256);
  strcpy(meta->general->sensor, header->mission);
  strcpy(meta->general->sensor_name, info->sensor);
  strcpy(meta->general->mode, info->elevationBeamConfiguration);
  strcpy(meta->general->receiving_station, info->receivingStation);
  strcpy(meta->general->processor, header->generationSystem);
  if (info->imageDataType == DETECTED_DATA_TYPE &&
      info->imageDataDepth == 8)
    meta->general->data_type = ASF_BYTE;
  else if (info->imageDataType == DETECTED_DATA_TYPE &&
	   info->imageDataDepth == 16)
    meta->general->data_type = INTEGER16;
  else if (info->imageDataType == DETECTED_DATA_TYPE &&
	   info->imageDataDepth == 32)
    // assumption here is that we are not dealing with INTERGER32
    meta->general->data_type = REAL32;
  else if (info->imageDataType == DETECTED_DATA_TYPE &&
	   info->imageDataDepth == 64)
    meta->general->data_type = REAL64;
  else if (info->imageDataType == COMPLEX_DATA_TYPE &&
	   info->imageDataDepth == 8)
    meta->general->data_type = COMPLEX_BYTE;
  else if (info->imageDataType == COMPLEX_DATA_TYPE &&
	   info->imageDataDepth == 16)
    meta->general->data_type = COMPLEX_INTEGER16;
  else if (info->imageDataType == COMPLEX_DATA_TYPE &&
	   info->imageDataDepth == 32)
    // assumption here is that we are not dealing with COMPLEX_INTEGER32
    meta->general->data_type = COMPLEX_REAL32;
  else if (info->imageDataType == COMPLEX_DATA_TYPE &&
	   info->imageDataDepth == 64)
    meta->general->data_type = COMPLEX_REAL64;
  if (info->imageDataType == RAW_DATA_TYPE)
    meta->general->image_data_type = RAW_IMAGE;
  else if (info->imageDataType == COMPLEX_DATA_TYPE)
    meta->general->image_data_type = COMPLEX_IMAGE;
  else if (info->imageDataType == DETECTED_DATA_TYPE)
    meta->general->image_data_type = AMPLITUDE_IMAGE;
  // more detailed mapping of imageDataType will probably need pixelValueID 
  // context
  if (strcmp_case(info->pixelValueID, "RADAR BRIGHTNESS") == 0)
    meta->general->radiometry = r_AMP;
  // dealing with any form of calibration not implemented yet
  dateTime2str(info->sceneCenterCoord.azimuthTimeUTC,
	       meta->general->acquisition_date);
  meta->general->orbit = info->absOrbit;
  if (info->orbitDirection == ASCENDING)  
    meta->general->orbit_direction = 'A';
  else if (info->orbitDirection == DESCENDING)
    meta->general->orbit_direction = 'D';
  meta->general->frame = setup->frameID;
  meta->general->band_count = comps->numLayers;
  strcpy(meta->general->bands, polLayer2str(comps->imageData[0].polLayer));
  for (ii=1; ii<comps->numLayers; ii++) {
    sprintf(str, ", %s", polLayer2str(comps->imageData[ii].polLayer));
    strcat(meta->general->bands, str);
  }
  meta->general->line_count = info->numberOfRows;
  meta->general->sample_count = info->numberOfColumns;
  meta->general->start_line = info->startRow;
  meta->general->start_sample = info->startColumn;
  meta->general->x_pixel_size = info->groundRangeResolution;
  meta->general->y_pixel_size = info->azimuthResolution;
  meta->general->center_latitude = info->sceneCenterCoord.lat;
  meta->general->center_longitude = info->sceneCenterCoord.lon;
  spheroid_type_t spheroid = WGS84_SPHEROID; // FIXME: needs to know reference
  spheroid_axes_lengths(spheroid,
			&meta->general->re_major, &meta->general->re_minor);
  meta->general->bit_error_rate = quality->imageDataQuality[0].bitErrorRate;
  meta->general->missing_lines = quality->imageDataQuality[0].missingLines;
  meta->general->no_data = (float) quality->imageDataQuality[0].noData;

  // SAR block
  meta->sar = meta_sar_init();
  if (info->projection == SLANTRANGE_PROJ)
    meta->sar->image_type = 'S';
  else if (info->projection == GROUNDRANGE_PROJ)
    meta->sar->image_type = 'G';
  else if (info->projection == MAP_PROJ)
    meta->sar->image_type = 'P';
  if (setup->lookDirection == RIGHT_LOOK)
    meta->sar->look_direction = 'R';
  else if (setup->lookDirection == LEFT_LOOK)
    meta->sar->look_direction = 'L';
  meta->sar->azimuth_look_count = info->azimuthLooks;
  meta->sar->range_look_count = info->rangeLooks;
  if (spec->imageCoordinateType == RAW_COORD)
    meta->sar->deskewed = FALSE;
  else if (spec->imageCoordinateType == ZERODOPPLER)
    meta->sar->deskewed = TRUE;
  meta->general->line_scaling = info->rowScaling;  
  meta->general->sample_scaling = info->columnScaling;
  meta->sar->range_time_per_pixel = info->columnSpacing;
  meta->sar->azimuth_time_per_pixel = info->rowSpacing;
  meta->sar->slant_shift = spec->slantRangeShift;
  //meta->sar->slant_range_first_pixel = info->rangeTimeFirstPixel * SPD_LIGHT;
  meta->sar->slant_range_first_pixel = spec->projectedSpacingSlantRange;
  meta->sar->wavelength = SPD_LIGHT / inst->centerFrequency;
  meta->sar->prf = spec->commonPRF;
  meta->sar->earth_radius = info->earthRadius;
  meta->sar->satellite_height = info->satelliteHeight;
  // meta->sar->satellite_binary_time;
  // meta->sar->satellite_clock_time;
  for (ii=0; ii<=proc->doppler[0].polynomialDegree; ii++)
    meta->sar->range_doppler_coefficients[ii] = 
      proc->doppler[0].coefficient[ii];
  meta->sar->azimuth_doppler_coefficients[0] = proc->doppler[0].coefficient[0];
  // meta->sar->chirp_rate
  // meta->sar->pulse_duration
  meta->sar->range_sampling_rate = spec->commonRSF;
  if (info->polarizationMode == SINGLE_POL)
    strcpy(meta->sar->polarization, "SINGLE");
  else if (info->polarizationMode == DUAL_POL)
    strcpy(meta->sar->polarization, "DUAL");
  else if (info->polarizationMode == QUAD_POL)
    strcpy(meta->sar->polarization, "QUAD");
  meta->sar->multilook = proc->multiLookedFlag;
  meta->sar->pitch = info->pitch;
  meta->sar->roll = info->roll;
  meta->sar->yaw = info->yaw;
  meta->sar->incid_a[0] = info->sceneCenterCoord.incidenceAngle;
  meta->sar->heading_angle = info->headingAngle;
  meta->sar->chirp_rate = proc->processingParameter[0].chirpRate;
  meta->sar->pulse_duration = proc->processingParameter[0].pulseDuration;

  // meta->projection
  // meta->transform
  // meta->airsar
  // meta->uavsar
  // meta->statistics

  int numVectors = platform->numStateVectors;
  meta->state_vectors = meta_state_vectors_init(numVectors);
  meta->state_vectors->vector_count = numVectors;
  ymd_date ymdStart, ymdSV;
  julian_date jd;
  hms_time hmsStart, hmsSV;
  ymdStart.year = info->startTimeUTC.year;
  ymdStart.month = info->startTimeUTC.month;
  ymdStart.day = info->startTimeUTC.day;
  hmsStart.hour = info->startTimeUTC.hour;
  hmsStart.min = info->startTimeUTC.min;
  hmsStart.sec = info->startTimeUTC.second;
  meta->state_vectors->year = info->startTimeUTC.year;
  date_ymd2jd(&ymdStart, &jd);
  meta->state_vectors->julDay = jd.jd;
  meta->state_vectors->second = date_hms2sec(&hmsStart);
  for (ii=0; ii<numVectors; ii++) {
    ymdSV.year = platform->stateVec[ii].timeUTC.year;
    ymdSV.month = platform->stateVec[ii].timeUTC.month;
    ymdSV.day = platform->stateVec[ii].timeUTC.day;
    hmsSV.hour = platform->stateVec[ii].timeUTC.hour;
    hmsSV.min = platform->stateVec[ii].timeUTC.min;
    hmsSV.sec = platform->stateVec[ii].timeUTC.second;
    meta->state_vectors->vecs[ii].time = 
      time_difference(&ymdSV, &hmsSV, &ymdStart, &hmsStart);
    meta->state_vectors->vecs[ii].vec.pos.x = platform->stateVec[ii].posX;
    meta->state_vectors->vecs[ii].vec.pos.y = platform->stateVec[ii].posY;
    meta->state_vectors->vecs[ii].vec.pos.z = platform->stateVec[ii].posZ;
    meta->state_vectors->vecs[ii].vec.vel.x = platform->stateVec[ii].velX;
    meta->state_vectors->vecs[ii].vec.vel.y = platform->stateVec[ii].velY;
    meta->state_vectors->vecs[ii].vec.vel.z = platform->stateVec[ii].velZ;
  }
  if (meta->sar->azimuth_time_per_pixel > 0)
    meta->sar->time_shift = 0.0;
  else
    meta->sar->time_shift = meta->state_vectors->vecs[numVectors-1].time;

  if (meta->sar->yaw == 0 ||
      !meta_is_valid_double(meta->sar->yaw))
  {
    meta->sar->yaw = meta_yaw(meta, meta->general->line_count/2.0,
                                    meta->general->sample_count/2.0);
  }
  /*
  // few calculations need state vectors
  meta->sar->earth_radius = 
    meta_get_earth_radius(meta, line_count/2, sample_count/2);
  // meta->sar->earth_radius_pp
  meta->sar->satellite_height =
    meta_get_sat_height(meta, meta->general->line_count/2,
			meta->general->sample_count/2);
  meta->sar->incid_a[0] = meta_incid(meta, line_count/2, sample_count/2)*R2D;
  */

  // Location block
  meta_get_corner_coords(meta);
/*
  meta->location = meta_location_init();
  for (ii=0; ii<4; ii++) {
    if (info->sceneCornerCoord[ii].refRow == 0 &&
	info->sceneCornerCoord[ii].refColumn == 0) {
      meta->location->lat_start_near_range = info->sceneCornerCoord[ii].lat;
      meta->location->lon_start_near_range = info->sceneCornerCoord[ii].lon;
    }
    else if (info->sceneCornerCoord[ii].refRow == 0 &&
	     info->sceneCornerCoord[ii].refColumn == sample_count) {
      meta->location->lat_start_far_range = info->sceneCornerCoord[ii].lat;
      meta->location->lon_start_far_range = info->sceneCornerCoord[ii].lon;
    }
    else if (info->sceneCornerCoord[ii].refRow == line_count &&
	     info->sceneCornerCoord[ii].refColumn == 0) {
      meta->location->lat_end_near_range = info->sceneCornerCoord[ii].lat;
      meta->location->lon_end_near_range = info->sceneCornerCoord[ii].lon;
    }
    else if (info->sceneCornerCoord[ii].refRow == line_count &&
	     info->sceneCornerCoord[ii].refColumn == sample_count) {
      meta->location->lat_end_far_range = info->sceneCornerCoord[ii].lat;
      meta->location->lon_end_far_range = info->sceneCornerCoord[ii].lon;
    }
  }
*/
  return meta;
}
Example #2
0
int main(int argc, char **argv)
{
  FILE *fpIn, *fpOut, *fpInList, *fpOutList, *fpXml;
  meta_parameters *meta;
  extern int currArg; /* from cla.h in asf.h... initialized to 1 */
  logflag = 0;
  
  // Parse command line args
  while (currArg < (argc-2)) {
    char *key=argv[currArg++];
    if (strmatch(key,"-log")) {
      sprintf(logFile, "%s", argv[currArg]);
      logflag = 1;
    }
    else {
      printf("\n   ***Invalid option:  %s\n\n",
	     argv[currArg-1]);
      usage(argv[0]);
    }
  }
  if ((argc-currArg) < 2) {
    printf("Insufficient arguments.\n");
    usage(argv[0]);
  }
  
  asfSplashScreen(argc, argv);
  
  char *listInFile = (char *) MALLOC(sizeof(char)*(strlen(argv[1])+1));
  strcpy(listInFile, argv[1]);
  char *outFile = (char *) MALLOC(sizeof(char)*(strlen(argv[2])+1));
  strcpy(outFile, argv[2]);
  
  // Setup file names
  char outDirName[512], outFileName[512];
  split_dir_and_file(outFile, outDirName, outFileName);
  char *tmpDir = (char *) MALLOC(sizeof(char)*512);
  sprintf(tmpDir, "%smeasures-", outDirName);
  char *tsdir = time_stamp_dir();
  strcat(tmpDir, tsdir);
  FREE(tsdir);
  create_clean_dir(tmpDir);
  char *isoStr = iso_date();

  // Read header information
  char inFile[512], imgFile[768], metaFile[768];
  char listOutFile[768], citation[50], start[30], end[30], first[30];
  char header[120], baseName[512], dirName[512], ext[5];
  float x_pix, y_pix, x_map_ll, y_map_ll, x_map_ur, y_map_ur, inc, cat;
  double lat, lon, height, x, y, z;
  int ii, kk, nFiles=0, num = 1, sample_count, line_count;
  image_data_type_t image_data_type;
  sprintf(listOutFile, "%s%crgps.xml", tmpDir, DIR_SEPARATOR);

  // Preparing map projection information
  project_parameters_t pps;
  projection_type_t proj_type;
  datum_type_t datum;
  spheroid_type_t spheroid;
  read_proj_file("polar_stereographic_north_ssmi.proj", 
     &pps, &proj_type, &datum, &spheroid);
  pps.ps.false_easting = 0.0;
  pps.ps.false_northing = 0.0;
  meta_projection *proj = meta_projection_init();
  proj->type = proj_type;
  proj->datum = HUGHES_DATUM;
  proj->spheroid = HUGHES_SPHEROID;
  proj->param = pps;
  strcpy(proj->units, "meters");
  proj->hem = 'N';
  spheroid_axes_lengths(spheroid, &proj->re_major, &proj->re_minor);
  FREE(proj);

  // Set up supplemental file names: water mask, lat/lon, x/y grids
  char maskFile[768], latFile[768], lonFile[768], xFile[768], yFile[768]; 
  sprintf(maskFile, "%s%cwater_mask.img", tmpDir, DIR_SEPARATOR);
  sprintf(latFile, "%s%clatitude.img", tmpDir, DIR_SEPARATOR);
  sprintf(lonFile, "%s%clongitude.img", tmpDir, DIR_SEPARATOR);
  sprintf(xFile, "%s%cxgrid.img", tmpDir, DIR_SEPARATOR);
  sprintf(yFile, "%s%cygrid.img", tmpDir, DIR_SEPARATOR);

  // Generating output XML file
  fpInList = FOPEN(listInFile, "r");
  fpOutList = FOPEN(listOutFile, "w");
  fprintf(fpOutList, "<netcdf>\n");
  fprintf(fpOutList, "  <data>\n");
  fprintf(fpOutList, "    <latitude>%s</latitude>\n", latFile);
  fprintf(fpOutList, "    <longitude>%s</longitude>\n", lonFile);
  fprintf(fpOutList, "    <xgrid>%s</xgrid>\n", xFile);
  fprintf(fpOutList, "    <ygrid>%s</ygrid>\n", yFile);
  fprintf(fpOutList, "    <mask>%s</mask>\n", maskFile);
  
  julian_date jdStart, jdEnd, jdRef;
  hms_time hms;
  hms.hour = 0;
  hms.min = 0;
  hms.sec = 0.0;

  asfPrintStatus("Working through the file list:\n");
  int myrFlag=FALSE, divFlag=FALSE, vrtFlag=FALSE, shrFlag=FALSE;
  int firstYear, firstDay, startYear, startDay, endYear, endDay;
  double westBoundLon, eastBoundLon, northBoundLat, southBoundLat;
  double minLat=90.0, maxLat=-90.0, minLon=180.0, maxLon=-180.0;

  while (fgets(inFile, 512, fpInList)) {

    chomp(inFile);
    char inDirName[512], inFileName[512];
    split_dir_and_file(inFile, inDirName, inFileName);

    // Preparing map projection information
    project_parameters_t pps;
    projection_type_t proj_type;
    datum_type_t datum;
    spheroid_type_t spheroid;
    read_proj_file("polar_stereographic_north_ssmi.proj", 
       &pps, &proj_type, &datum, &spheroid);
    pps.ps.false_easting = 0.0;
    pps.ps.false_northing = 0.0;
    meta_projection *proj = meta_projection_init();
    proj->type = proj_type;
    proj->datum = HUGHES_DATUM;
    proj->spheroid = HUGHES_SPHEROID;
    proj->param = pps;
    strcpy(proj->units, "meters");
    proj->hem = 'N';
    spheroid_axes_lengths(spheroid, &proj->re_major, &proj->re_minor);

    // Sort out dates
    startYear = subInt(inFileName, 0, 4);
    startDay = subInt(inFileName, 4, 3);
    endYear = subInt(inFileName, 8, 4);
    endDay = subInt(inFileName, 12, 3);
    if (nFiles == 0) {
      firstYear = startYear;
      firstDay = startDay;
    }
    sprintf(citation, "%d%03d to %d%03d", startYear, startDay, endYear, endDay);
    rgps2iso_date(startYear, (double) startDay, start);
    rgps2iso_date(endYear, (double) endDay, end);
    rgps2iso_date(firstYear, (double) firstDay, first);
    
    // Read header information
    FILE *fpIn = FOPEN(inFile, "r");
    fgets(header, 100, fpIn);
    sscanf(header, "%f %f %f %f %f %f", &x_pix, &y_pix, &x_map_ll, &y_map_ll, 
      &x_map_ur, &y_map_ur);
    fgets(header, 100, fpIn);
    int params = sscanf(header, "%f %f %d %d", 
      &inc, &cat, &sample_count, &line_count);
    if (params == 3) {
      sscanf(header, "%f %d %d", &cat, &sample_count, &line_count);
      inc = 0;
    }
    else if (params == 2) {
      sscanf(header, "%d %d", &sample_count, &line_count);
      inc = 0;
      cat = 1;
    }
    num = (int) cat;
    if (num > 1)
      asfPrintError("Multiband imagery (%s) not supported for netCDF "
        "generation!\n", inFile);

    /*  
    printf("x_pix: %f, y_pix: %f\n", x_pix, y_pix);
    printf("x_map_ll: %f, y_map_ll: %f\n", x_map_ll, y_map_ll);
    printf("x_map_ur: %f, y_map_ur: %f\n", x_map_ur, y_map_ur);
    printf("sample_count: %d, line_count: %d\n\n", sample_count, line_count);
    */
      
    // Check extension
    split_base_and_ext(inFileName, 1, '.', baseName, ext);
    asfPrintStatus("Processing %s ...\n", inFileName);
    sprintf(imgFile, "%s%c%s_%s.img", tmpDir, DIR_SEPARATOR, baseName, &ext[1]);
    sprintf(metaFile, "%s%c%s_%s.meta", tmpDir, DIR_SEPARATOR, baseName, 
      &ext[1]);
    
    jdRef.year = firstYear;
    jdRef.jd = 1;
    jdStart.year = startYear;
    jdStart.jd = startDay;
    jdEnd.year = endYear;
    jdEnd.jd = endDay;
    double startSec = date2sec(&jdStart, &hms) - date2sec(&jdRef, &hms);
    double endSec = date2sec(&jdEnd, &hms) - date2sec(&jdRef, &hms);
    if (strcmp_case(ext, ".MYR") == 0) {
      fprintf(fpOutList, "    <multiyear_ice_fraction start=\"%.0f\" end=\"%.0f"
        "\">%s</multiyear_ice_fraction>\n", startSec, endSec, imgFile);
      image_data_type = MULTIYEAR_ICE_FRACTION;
      myrFlag = TRUE;
    }
    else if (strcmp_case(ext, ".DIV") == 0) {
      fprintf(fpOutList, "    <divergence start=\"%.0f\" end=\"%.0f\">%s"
        "</divergence>\n", startSec, endSec, imgFile);
      image_data_type = DIVERGENCE;
      divFlag = TRUE;
    }
    else if (strcmp_case(ext, ".VRT") == 0) {
      fprintf(fpOutList, "    <vorticity start=\"%.0f\" end=\"%.0f\">%s"
        "</vorticity>\n", startSec, endSec, imgFile);
      image_data_type = VORTICITY;
      vrtFlag = TRUE;
    }
    else if (strcmp_case(ext, ".SHR") == 0) {
      fprintf(fpOutList, "    <shear start=\"%.0f\" end=\"%.0f\">%s</shear>", 
        startSec, endSec, imgFile);
      image_data_type = SHEAR;
      shrFlag = TRUE;
    }

    // Generate basic metadata
    meta = raw_init();
    meta->general->line_count = line_count;
    meta->general->sample_count = sample_count;
    meta->general->band_count = 1;
    meta->general->data_type = REAL32;
    meta->general->image_data_type = image_data_type;
    strcpy(meta->general->basename, inFile);
    meta->general->x_pixel_size = x_pix*1000.0;
    meta->general->y_pixel_size = y_pix*1000.0;
    meta->general->start_line = 0;
    meta->general->start_sample = 0;
    meta->general->no_data = MAGIC_UNSET_DOUBLE;
    strcpy(meta->general->sensor, "RGPS MEaSUREs");
    char *tmp = image_data_type2str(meta->general->image_data_type);
    sprintf(meta->general->bands, "%s", lc(tmp));
    FREE(tmp);
    sprintf(meta->general->acquisition_date, "%s", baseName);
    
    // Sort out map projection
    proj->startX = x_map_ll*1000.0;
    proj->startY = y_map_ur*1000.0;
    proj->perX = x_pix*1000.0;
    proj->perY = -y_pix*1000.0;
    meta->projection = proj;
    meta_write(meta, metaFile);
    strcpy(meta->general->bands, "water mask");
    sprintf(metaFile, "%s%cwater_mask.meta", tmpDir, DIR_SEPARATOR);
    meta_write(meta, metaFile);  
    sprintf(metaFile, "%s%c%s_%s.meta", tmpDir, DIR_SEPARATOR, baseName, 
      &ext[1]);
    
    float *floatBuf = (float *) MALLOC(sizeof(float)*sample_count);

    // Write gridded data to ASF internal format
    fpOut = FOPEN(imgFile, "wb");
    for (ii=0; ii<line_count; ii++) {
      for (kk=0; kk<sample_count; kk++) {
	      ASF_FREAD(&floatBuf[kk], sizeof(float), 1, fpIn);
	      ieee_big32(floatBuf[kk]);
        if (floatBuf[kk] > 10000000000.0 || 
          FLOAT_EQUIVALENT(floatBuf[kk], 10000000000.0))
          floatBuf[kk] = MAGIC_UNSET_DOUBLE;
      }
      put_float_line(fpOut, meta, line_count-ii-1, floatBuf);
    }
    FCLOSE(fpOut);
    FREE(floatBuf);
    
    double lat1, lon1, lat2, lon2, lat3, lon3, lat4, lon4;
    proj_to_latlon(proj, x_map_ll*1000.0, y_map_ll*1000.0, 0.0, 
      &lat1, &lon1, &height);
    proj_to_latlon(proj, x_map_ll*1000.0, y_map_ur*1000.0, 0.0, 
      &lat2, &lon2, &height);
    proj_to_latlon(proj, x_map_ur*1000.0, y_map_ur*1000.0, 0.0, 
      &lat3, &lon3, &height);
    proj_to_latlon(proj, x_map_ur*1000.0, y_map_ll*1000.0, 0.0, 
      &lat4, &lon4, &height);
    westBoundLon = minValue(lon1*R2D, lon2*R2D, lon3*R2D, lon4*R2D);
    eastBoundLon = maxValue(lon1*R2D, lon2*R2D, lon3*R2D, lon4*R2D);
    northBoundLat = maxValue(lat1*R2D, lat2*R2D, lat3*R2D, lat4*R2D);
    southBoundLat = minValue(lat1*R2D, lat2*R2D, lat3*R2D, lat4*R2D);
    if (westBoundLon < minLon)
      minLon = westBoundLon;
    if (eastBoundLon > maxLon)
      maxLon = eastBoundLon;
    if (southBoundLat < minLat)
      minLat = southBoundLat;
    if (northBoundLat > maxLat)
      maxLat = northBoundLat;

    meta_free(meta);
    nFiles++;
  }
  FCLOSE(fpInList);
  
  fprintf(fpOutList, "  </data>\n");
  fprintf(fpOutList, "  <metadata>\n");
  fprintf(fpOutList, "    <time>\n");
  fprintf(fpOutList, "      <axis type=\"string\" definition=\"name of axis\">T"
    "</axis>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">serial date</long_name>\n");
  fprintf(fpOutList, "      <references type=\"string\" definition=\"reference "
    "of the value\">start time of 3-day average</references>\n");
  fprintf(fpOutList, "      <standard_name type=\"string\" definition=\"name "
    "used to identify the physical quantity\">time</standard_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">seconds since %d-01-01T00:00:00Z</units>\n",
    firstYear);
  fprintf(fpOutList, "      <bounds type=\"string\" definition=\"variable "
    "containing data range\">time_bounds</bounds>\n");
  fprintf(fpOutList, "      <FillValue type=\"double\" definition=\"default "
    "value\">0</FillValue>\n");
  fprintf(fpOutList, "    </time>\n");
  fprintf(fpOutList, "    <time_bounds>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">serial date</long_name>\n");
  fprintf(fpOutList, "      <references type=\"string\" definition=\"reference "
    "of the value\">start and end time of 3-day average</references>\n");
  fprintf(fpOutList, "      <standard_name type=\"string\" definition=\"name "
    "used to identify the physical quantity\">time</standard_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">seconds since %d-01-01T00:00:00Z</units>\n",
    firstYear);
  fprintf(fpOutList, "      <FillValue type=\"double\" definition=\"default "
    "value\">0</FillValue>\n");
  fprintf(fpOutList, "    </time_bounds>\n");
  fprintf(fpOutList, "    <latitude>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">latitude</long_name>\n");
  fprintf(fpOutList, "      <standard_name type=\"string\" definition=\"name "
    "used to identify the physical quantity\">latitude</standard_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">degrees_north</units>\n");
  fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
    "value\">-999</FillValue>\n");
  fprintf(fpOutList, "      <valid_min type=\"float\" definition=\"minimum "
    "valid value\">-90.0</valid_min>\n");
  fprintf(fpOutList, "      <valid_max type=\"float\" definition=\"minimum "
    "valid value\">90.0</valid_max>\n");
  fprintf(fpOutList, "    </latitude>\n");
  fprintf(fpOutList, "    <longitude>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">longitude</long_name>\n");
  fprintf(fpOutList, "      <standard_name type=\"string\" definition=\"name "
    "used to identify the physical quantity\">longitude</standard_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">degrees_east</units>\n");
  fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
    "value\">-999</FillValue>\n");
  fprintf(fpOutList, "      <valid_min type=\"float\" definition=\"minimum "
    "valid value\">-180.0</valid_min>\n");
  fprintf(fpOutList, "      <valid_max type=\"float\" definition=\"minimum "
    "valid value\">180.0</valid_max>\n");
  fprintf(fpOutList, "    </longitude>\n");
  fprintf(fpOutList, "    <xgrid>\n");
  fprintf(fpOutList, "      <axis type=\"string\" definition=\"name of axis\">X"
    "</axis>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">projection_grid_x_center</long_name>\n");
  fprintf(fpOutList, "      <standard_name type=\"string\" definition=\"name "
    "used to identify the physical quantity\">projection_x_coordinate"
    "</standard_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">meters</units>\n");
  fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
    "value\">NaN</FillValue>\n");
  fprintf(fpOutList, "    </xgrid>\n");
  fprintf(fpOutList, "    <ygrid>\n");
  fprintf(fpOutList, "      <axis type=\"string\" definition=\"name of axis\">Y"
    "</axis>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">projection_grid_y_center</long_name>\n");
  fprintf(fpOutList, "      <standard_name type=\"string\" definition=\"name "
    "used to identify the physical quantity\">projection_y_coordinate"
    "</standard_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">meters</units>\n");
  fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
    "value\">NaN</FillValue>\n");
  fprintf(fpOutList, "    </ygrid>\n");
  fprintf(fpOutList, "    <Polar_Stereographic>\n");
  fprintf(fpOutList, "      <grid_mapping_name>polar_stereographic"
    "</grid_mapping_name>\n");
  fprintf(fpOutList, "      <straight_vertical_longitude_from_pole>%.1f"
    "</straight_vertical_longitude_from_pole>\n", pps.ps.slon);
  fprintf(fpOutList, "      <longitude_of_central_meridian>90.0"
    "</longitude_of_central_meridian>\n");
  fprintf(fpOutList, "      <standard_parallel>%.1f</standard_parallel>\n", 
    pps.ps.slat);
  fprintf(fpOutList, "      <false_easting>%.1f</false_easting>\n", 
    pps.ps.false_easting);
  fprintf(fpOutList, "      <false_northing>%.1f</false_northing>\n",
    pps.ps.false_northing);
  fprintf(fpOutList, "      <projection_x_coordinate>xgrid"
    "</projection_x_coordinate>\n");
  fprintf(fpOutList, "      <projection_y_coordinate>ygrid"
    "</projection_y_coordinate>\n");
  fprintf(fpOutList, "      <units>meters</units>\n");
  fprintf(fpOutList, "    </Polar_Stereographic>\n");
  fprintf(fpOutList, "    <mask>\n");
  fprintf(fpOutList, "      <coordinates type=\"string\" definition=\""
    "coordinate reference\">ygrid xgrid</coordinates>\n");
  fprintf(fpOutList, "      <grid_mapping type=\"string\" definition=\"\">"
    "Polar_Stereographic</grid_mapping>\n");
  fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
    "descriptive name\">projection_grid_y_center</long_name>\n");
  fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
    "dimensional quantity\">1</units>\n");
  fprintf(fpOutList, "      <units_description type=\"string\" definition=\""
    "descriptive information about dimensionless quantity\">unitless"
    "</units_description>\n");
  fprintf(fpOutList, "      <FillValue type=\"int\" definition=\"default "
    "value\">0</FillValue>\n");
  fprintf(fpOutList, "    </mask>\n");
  if (myrFlag) {
    fprintf(fpOutList, "    <multiyear_ice_fraction>\n");
    fprintf(fpOutList, "      <cell_methods type=\"string\" definition=\""
      "characteristic of a field that is represented by cell values\">area: "
      "multiyear ice fraction value</cell_methods>\n");
    fprintf(fpOutList, "      <coordinates type=\"string\" definition=\""
      "coordinate reference\">ygrid xgrid</coordinates>\n");
    fprintf(fpOutList, "      <grid_mapping type=\"string\" definition=\"\">"
      "Polar_Stereographic</grid_mapping>\n");
    fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
      "descriptive name\">RGPS MEaSUREs multiyear ice fraction</long_name>\n");
    fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
      "dimensional quantity\">1</units>\n");
    fprintf(fpOutList, "      <units_description type=\"string\" definition=\""
      "descriptive information about dimensionless quantity\">unitless"
      "</units_description>\n");
    fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
      "value\">NaN</FillValue>\n");
    fprintf(fpOutList, "    </multiyear_ice_fraction>\n");
  }
  if (divFlag) {
    fprintf(fpOutList, "    <divergence>\n");
    fprintf(fpOutList, "      <cell_methods type=\"string\" definition=\""
      "characteristic of a field that is represented by cell values\">area: "
      "divergence value</cell_methods>\n");
    fprintf(fpOutList, "      <coordinates type=\"string\" definition=\""
      "coordinate reference\">ygrid xgrid</coordinates>\n");
    fprintf(fpOutList, "      <grid_mapping type=\"string\" definition=\"\">"
      "Polar_Stereographic</grid_mapping>\n");
    fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
      "descriptive name\">RGPS MEaSUREs divergence</long_name>\n");
    fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
      "dimensional quantity\">1</units>\n");
    fprintf(fpOutList, "      <units_description type=\"string\" definition=\""
      "descriptive information about dimensionless quantity\">unitless"
      "</units_description>\n");
    fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
      "value\">NaN</FillValue>\n");
    fprintf(fpOutList, "    </divergence>\n");
  }
  if (vrtFlag) {
    fprintf(fpOutList, "    <vorticity>\n");
    fprintf(fpOutList, "      <cell_methods type=\"string\" definition=\""
      "characteristic of a field that is represented by cell values\">area: "
      "vorticity value</cell_methods>\n");
    fprintf(fpOutList, "      <coordinates type=\"string\" definition=\""
      "coordinate reference\">ygrid xgrid</coordinates>\n");
    fprintf(fpOutList, "      <grid_mapping type=\"string\" definition=\"\">"
      "Polar_Stereographic</grid_mapping>\n");
    fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
      "descriptive name\">RGPS MEaSUREs vorticity</long_name>\n");
    fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
      "dimensional quantity\">1</units>\n");
    fprintf(fpOutList, "      <units_description type=\"string\" definition=\""
      "descriptive information about dimensionless quantity\">unitless"
      "</units_description>\n");
    fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
      "value\">NaN</FillValue>\n");
    fprintf(fpOutList, "    </vorticity>\n");
  }
  if (shrFlag) {
    fprintf(fpOutList, "    <shear>\n");
    fprintf(fpOutList, "      <cell_methods type=\"string\" definition=\""
      "characteristic of a field that is represented by cell values\">area: "
      "shear value</cell_methods>\n");
    fprintf(fpOutList, "      <coordinates type=\"string\" definition=\""
      "coordinate reference\">ygrid xgrid</coordinates>\n");
    fprintf(fpOutList, "      <grid_mapping type=\"string\" definition=\"\">"
      "Polar_Stereographic</grid_mapping>\n");
    fprintf(fpOutList, "      <long_name type=\"string\" definition=\"long "
      "descriptive name\">RGPS MEaSUREs shear</long_name>\n");
    fprintf(fpOutList, "      <units type=\"string\" definition=\"unit of "
      "dimensional quantity\">1</units>\n");
    fprintf(fpOutList, "      <units_description type=\"string\" definition=\""
      "descriptive information about dimensionless quantity\">unitless"
      "</units_description>\n");
    fprintf(fpOutList, "      <FillValue type=\"float\" definition=\"default "
      "value\">NaN</FillValue>\n");
    fprintf(fpOutList, "    </shear>\n");
  }
  fprintf(fpOutList, "  </metadata>\n");
  fprintf(fpOutList, "  <parameter>\n");
  if (myrFlag)
    fprintf(fpOutList, "    <multiyear_ice_fraction type=\"float\"/>\n");
  if (divFlag)
    fprintf(fpOutList, "    <divergence type=\"float\"/>\n");
  if (vrtFlag)
    fprintf(fpOutList, "    <vorticity type=\"float\"/>\n");
  if (shrFlag)
    fprintf(fpOutList, "    <shear type=\"float\"/>\n");
  fprintf(fpOutList, "  </parameter>\n");
  
  char startStr[15], endStr[15];
  jdStart.year = firstYear;
  jdStart.jd = firstDay;
  jdEnd.year = endYear;
  jdEnd.jd = endDay;
  jd2date(&jdStart, startStr);
  jd2date(&jdEnd, endStr);
  if (firstYear != endYear || firstDay != endDay)
    sprintf(citation, "%s to %s", startStr, endStr);
  else
    sprintf(citation, "%s", startStr);
  fprintf(fpOutList, "  <root>\n");
  fprintf(fpOutList, "    <Conventions>CF-1.6</Conventions>\n");
  fprintf(fpOutList, "    <institution>Alaska Satellite Facility</institution>\n");
  fprintf(fpOutList, "    <title>Kwok, Ron. 2008. MEaSUREs Small-Scale Kinematics"
    " of Arctic Ocean Sea Ice, Version 01, %s. Jet Propulsion Laboratory "
    "Pasadena, CA USA and Alaska Satellite Facility Fairbanks, AK USA. "
    "Digital media.</title>\n", citation);
  fprintf(fpOutList, "    <source>Products derived from RADARSAT-1 SWB imagery at "
    "100 m resolution</source>\n");
  fprintf(fpOutList, "    <comment>Imagery the products are derived from: Copyright "
    "Canadian Space Agency (1996 to 2008)</comment>\n");
  fprintf(fpOutList, "    <reference>Documentation available at: www.asf.alaska.edu"
    "</reference>\n");
  fprintf(fpOutList, "    <history>%s: netCDF file created.</history>\n", isoStr);
  fprintf(fpOutList, "  </root>\n");
  fprintf(fpOutList, "</netcdf>\n");
  FCLOSE(fpOutList);

  // Generate supplemental files: water mask, lat/lon, x/y grids
  asfPrintStatus("Generating supplemental files ...\n");
  float *floatBuf = (float *) MALLOC(sizeof(float)*sample_count);
  float *maskBuf = (float *) MALLOC(sizeof(float)*sample_count);
  float *latBuf = (float *) MALLOC(sizeof(float)*sample_count);
  float *lonBuf = (float *) MALLOC(sizeof(float)*sample_count);
  float *xBuf = (float *) MALLOC(sizeof(float)*sample_count);
  float *yBuf = (float *) MALLOC(sizeof(float)*sample_count);
  meta = meta_read(metaFile);
  
  fpIn = FOPEN(inFile, "r");
  fgets(header, 100, fpIn);
  sscanf(header, "%f %f %f %f %f %f", &x_pix, &y_pix, &x_map_ll, &y_map_ll, 
    &x_map_ur, &y_map_ur);
  fgets(header, 100, fpIn);
  sscanf(header, "%d %d", &sample_count, &line_count);
  
  FILE *fpMask = FOPEN(maskFile, "wb");
  FILE *fpLat = FOPEN(latFile, "wb");
  FILE *fpLon = FOPEN(lonFile, "wb");
  FILE *fpXgrid = FOPEN(xFile, "wb");
  FILE *fpYgrid = FOPEN(yFile, "wb");
  for (ii=0; ii<line_count; ii++) {
    for (kk=0; kk<sample_count; kk++) {
      ASF_FREAD(&floatBuf[kk], sizeof(float), 1, fpIn);
      ieee_big32(floatBuf[kk]);
    }
    for (kk=0; kk<sample_count; kk++) {
      meta_get_latLon(meta, line_count-ii-1, kk, 0.0, &lat, &lon);
      latlon_to_proj(meta->projection, 'R', lat*D2R, lon*D2R, 0.0, &x, &y, &z);
      latBuf[kk] = lat;
      lonBuf[kk] = lon;
      xBuf[kk] = x;
      yBuf[kk] = y;
      if (floatBuf[kk] < 10000000000.0) {
        maskBuf[kk] = 1.0;
      }
      else if (floatBuf[kk] > 10000000000.0) {
        maskBuf[kk] = 1.0;
      }
      else {
        maskBuf[kk] = 0.0;
      }
    }
    put_float_line(fpMask, meta, line_count-ii-1, maskBuf);
    put_float_line(fpLat, meta, line_count-ii-1, latBuf);
    put_float_line(fpLon, meta, line_count-ii-1, lonBuf);
    put_float_line(fpXgrid, meta, line_count-ii-1, xBuf);
    put_float_line(fpYgrid, meta, line_count-ii-1, yBuf);
  }
  FCLOSE(fpIn);
  FCLOSE(fpMask);
  FCLOSE(fpLat);
  FCLOSE(fpLon);
  FREE(floatBuf);
  FREE(maskBuf);
  FREE(latBuf);
  FREE(lonBuf);
  FREE(xBuf);
  FREE(yBuf);
  meta_write(meta, latFile);
  meta_write(meta, lonFile);
  meta_write(meta, xFile);
  meta_write(meta, yFile);

  // Write ISO meatadata for netCDF
  asfPrintStatus("Generating metadata for netCDF file ...\n");

  char *ncXmlBase = get_basename(outFile);
  char *ncXmlFile = appendExt(outFile, ".xml");
  fpXml = FOPEN(ncXmlFile, "w");
  fprintf(fpXml, "<rgps>\n");
  fprintf(fpXml, "  <granule>%s</granule>\n", ncXmlBase);
  fprintf(fpXml, "  <metadata_creation>%s</metadata_creation>\n", isoStr);
  fprintf(fpXml, "  <metadata>\n");
  fprintf(fpXml, "    <product>\n");
  fprintf(fpXml, "      <file type=\"string\" definition=\"name of product "
    "file\">%s.nc</file>\n", ncXmlBase);
  if (divFlag && vrtFlag && shrFlag)
    fprintf(fpXml, "      <type type=\"string\" definition=\"product type\">"
    "divergence, vorticity, shear</type>\n");
  else if (myrFlag)
    fprintf(fpXml, "      <type type=\"string\" definition=\"product type\">"
    "multiyear ice fraction</type>\n");
  fprintf(fpXml, "      <format type=\"string\" definition=\"name of the data "
    "format\">netCDF</format>\n");

  fpInList = FOPEN(listInFile, "r");
  while (fgets(inFile, 512, fpInList)) {
    chomp(inFile);
    split_dir_and_file(inFile, dirName, baseName);
    fprintf(fpXml, "      <source type=\"string\" definition=\"name of the data"
    " source\">%s</source>\n", baseName);
  }
  FCLOSE(fpInList);

  fprintf(fpXml, "      <cell_size_x type=\"double\" definition=\"cell size "
    "in x direction\" units=\"m\">%.2f</cell_size_x>\n", x_pix*1000.0);
  fprintf(fpXml, "      <cell_size_y type=\"double\" definition=\"cell size "
    "in y direction\" units=\"m\">%.2f</cell_size_y>\n", y_pix*1000.0);
  fprintf(fpXml, "      <map_x_lower_left type=\"double\" definition=\"x "
    "coordinate of lower left corner\" units=\"m\">%.6f</map_x_lower_left>\n",
    x_map_ll*1000.0);
  fprintf(fpXml, "      <map_y_lower_left type=\"double\" definition=\"y "
    "coordinate of lower left corner\" units=\"m\">%.6f</map_y_lower_left>\n",
    y_map_ll*1000.0);
  fprintf(fpXml, "      <map_x_upper_right type=\"double\" definition=\"x "
    "coordinate of upper right corner\" units=\"m\">%.6f</map_x_upper_right>"
    "\n", x_map_ur*1000.0);
  fprintf(fpXml, "      <map_y_upper_right type=\"double\" definition=\"y "
    "coordinate of upper right corner\" units=\"m\">%.6f</map_y_upper_right>"
    "\n", y_map_ur*1000.0);
  fprintf(fpXml, "      <cell_dimension_x type=\"int\" definition=\"cell "
    "dimension in x direction\">%d</cell_dimension_x>\n", 
    sample_count);
  fprintf(fpXml, "      <cell_dimension_y type=\"int\" definition=\"cell "
    "dimension in y direction\">%d</cell_dimension_y>\n",
      line_count);
  fprintf(fpXml, "      <projection_string type=\"string\" definition=\"map "
    "projection information as well known text\">%s</projection_string>\n", 
  meta2esri_proj(meta, NULL));
  fprintf(fpXml, "    </product>\n");
  fprintf(fpXml, "  </metadata>\n");
  fprintf(fpXml, "  <extent>\n");
  fprintf(fpXml, "    <product>\n");
  fprintf(fpXml, "      <westBoundLongitude>%.5f</westBoundLongitude>\n",
    minLon);
  fprintf(fpXml, "      <eastBoundLongitude>%.5f</eastBoundLongitude>\n",
    maxLon);
  fprintf(fpXml, "      <northBoundLatitude>%.5f</northBoundLatitude>\n",
    maxLat);
  fprintf(fpXml, "      <southBoundLatitude>%.5f</southBoundLatitude>\n",
    minLat);
  fprintf(fpXml, "      <start_datetime>%s</start_datetime>\n", first);
  fprintf(fpXml, "      <end_datetime>%s</end_datetime>\n", end);
  fprintf(fpXml, "    </product>\n");
  fprintf(fpXml, "  </extent>\n");
  fprintf(fpXml, "</rgps>\n");
  FCLOSE(fpXml);
  FREE(ncXmlBase);
  FREE(ncXmlFile);
  meta_free(meta);

  // Export to netCDF
  asfPrintStatus("Exporting to netCDF file ...\n");
  export_netcdf_xml(listOutFile, outFile);

  // Clean up
  remove_dir(tmpDir);
  FREE(tmpDir);
  FREE(outFile);
  FREE(listInFile);
  FREE(isoStr);

  return 0;
}
meta_parameters *read_meta_gridfloat_ext(char *inBaseName, char *flt_file,
					 int *column_count, int *row_count)
{
    // all three of these must be present
    char *hdr_file = appendExt(inBaseName, ".hdr");
    char *prj_file = appendExt(inBaseName, ".prj");
    flt_file = appendExt(inBaseName, ".flt");

    if (!fileExists(flt_file) || !fileExists(hdr_file) || !fileExists(prj_file))
    {
        asfPrintError(
            "The BIL and/or associated metadata files were not found:\n"
            "  FLT File: %s %s\n"
            "  PRJ File: %s %s\n"
            "  HDR File: %s %s\n",
          flt_file, fileExists(flt_file) ? "Found" : "NOT FOUND",
          prj_file, fileExists(prj_file) ? "Found" : "NOT FOUND",
          hdr_file, fileExists(hdr_file) ? "Found" : "NOT FOUND");
    }

    asfPrintStatus("Parsing %s ...\n", hdr_file);

    // Info we'll get from the header file
    int nrows=-1;
    int ncols=-1;
    double cell_size = 0;
    double lat_ll = -999;
    double lon_ll = -999;
    double nodata = -9999;
    int msbfirst = FALSE;

    // Read header file
    char line[1024], value[1024];
    FILE *fp = FOPEN(hdr_file, "r");
    while (NULL!=fgets(line, 1024, fp)) {
        if (matches(line, "ncols")) {
            get_value(line, value);
            ncols = atoi(value);
        } else if (matches(line, "nrows")) {
            get_value(line, value);
            nrows = atoi(value);
        } else if (matches(line, "xllcorner")) {
            get_value(line, value);
            lon_ll = atof(value);
        } else if (matches(line, "yllcorner")) {
            get_value(line, value);
            lat_ll = atof(value);
        } else if (matches(line, "cellsize")) {
            get_value(line, value);
            cell_size = atof(value);
        } else if (matches(line, "NODATA_value")) {
            get_value(line, value);
            nodata = atof(value);
        } else if (matches(line, "byteorder")) {
            get_value(line, value);
            if (strcmp(value, "LSBFIRST") == 0) {
                msbfirst = FALSE;
            } else if (strcmp(value, "MSBFIRST") == 0) {
                msbfirst = TRUE;
            } else {
                asfPrintError("Unsupported byte order (should be 'LSBFIRST' or 'MSBFIRST'): %s\n",
                    value);
            }
        }
    }
    fclose(fp);

    if (nrows < 0 || ncols < 0) {
        asfPrintError(
            "Header file did not contain Row/Column infomation.\n"
            "It is a valid .HDR file?\n");
    }

    // PRJ File
    asfPrintStatus("Parsing %s ...\n", prj_file);

    datum_type_t datum=UNKNOWN_DATUM;
    spheroid_type_t spheroid=UNKNOWN_SPHEROID;

    fp = FOPEN(prj_file, "r");
    while (NULL!=fgets(line, 1024, fp)) {
        if (matches(line, "Projection")) {
            get_value(line, value);
            if (strcmp(value, "GEOGRAPHIC") != 0)
                asfPrintError("Unsupported byte order (should be GEOGRAPHIC): %s\n",
                    value);
        } else if (matches(line, "Units")) {
            get_value(line, value);
            if (strcmp(value, "DD") != 0)
                asfPrintError("Unsupported Units (should be DD): %s\n",
                    value);
        } else if (matches(line, "Zunits")) {
            get_value(line, value);
            if (strcmp(value, "METERS") != 0)
                asfPrintError("Unsupported Zunits (should be METERS): %s\n",
                    value);
        } else if (matches(line, "Datum")) {
            get_value(line, value);
            if (strcmp_case(value, "NAD27") == 0)
                datum = NAD27_DATUM;
            else if (strcmp_case(value, "NAD83") == 0)
                datum = NAD83_DATUM;
            else if (strcmp_case(value, "WGS83") == 0)
                datum = WGS84_DATUM;
            else
                asfPrintError(
                    "Unsupported Datum (should be NAD27, NAD83, or WGS84): %s\n",
                    value);
        } else if (matches(line, "Spheroid")) {
            get_value(line, value);
            if (strcmp_case(value, "CLARKE1866") == 0)
                spheroid = CLARKE1866_SPHEROID;
            else if (strcmp_case(value, "BESSEL") == 0)
                spheroid = BESSEL_SPHEROID;
            else if (strcmp_case(value, "CLARKE1880") == 0)
                spheroid = CLARKE1880_SPHEROID;
            else if (strcmp_case(value, "GEM6") == 0)
                spheroid = GEM6_SPHEROID;
            else if (strcmp_case(value, "GEM10C") == 0)
                spheroid = GEM10C_SPHEROID;
            else if (strcmp_case(value, "GRS1980") == 0)
                spheroid = GRS1980_SPHEROID;
            else if (strcmp_case(value, "WGS72") == 0)
                spheroid = WGS72_SPHEROID;
            else if (strcmp_case(value, "WGS84") == 0)
                spheroid = WGS84_SPHEROID;
            else if (strcmp_case(value, "INTERNATIONAL1924") == 0)
                spheroid = INTERNATIONAL1924_SPHEROID;
            else if (strcmp_case(value, "INTERNATIONAL1967") == 0)
                spheroid = INTERNATIONAL1967_SPHEROID;
            else
                asfPrintError("Unsupported Spheroid: %s\n", value);
        }
    }
    fclose(fp);

    meta_parameters *meta = raw_init();
    meta->projection = meta_projection_init();
    meta->location = meta_location_init();

    // aliases
    meta_general *mg = meta->general;
    meta_projection *mp = meta->projection;
    meta_location *ml = meta->location;

    // populate general block info
    mg->data_type = REAL32;
    mg->image_data_type = DEM; //presumably!?

    mg->no_data = nodata;

    // these are supposed to be in meters
    // currently, cell_size_* is in arcsecs... so here is a kludgey
    // calculation, to round to the nearest 10m. usgs data is either
    // 30, 60, or 90 meter.
    // Note that the projection block will have the actual pixel size
    int cell_size_m = 10 * (int)(11131.95 * cell_size + .5);
    if (cell_size_m != 30 && cell_size_m != 60 && cell_size_m != 90)
    {
        asfPrintWarning("Unexpected pixel size of %dm (%.10f degree) detected.\n"
            "USGS Seamless data should be 30, 60, or 90 meter.\n",
            cell_size_m, cell_size);
    }
    mg->x_pixel_size = cell_size_m;
    mg->y_pixel_size = cell_size_m;

    mg->line_count = nrows;
    mg->sample_count = ncols;
    mg->band_count = 1;
    mg->start_line = 0;
    mg->start_sample = 0;

    char *basename = get_basename(inBaseName);
    strcpy(mg->basename, basename);
    free(basename);

    strcpy(mg->sensor, "USGS Seamless data (e.g., NED, STRM)");
    strcpy(mg->mode, "N/A");
    strcpy(mg->processor, "Unknown");

    mg->center_latitude = lat_ll + cell_size * ncols / 2.0;
    mg->center_longitude = lon_ll + cell_size * nrows / 2.0;

    // populate projection block info

    mp->type = LAT_LONG_PSEUDO_PROJECTION;
    mp->startX = lon_ll;
    mp->startY = lat_ll + cell_size * nrows;
    mp->perX = cell_size;
    mp->perY = -cell_size;
    strcpy(mp->units, "degrees");
    mp->hem = mg->center_latitude > 0 ? 'N' : 'S';

    // the datum for NED is NAD83, for SRTM it is WGS84
    mp->datum = datum;
    mp->spheroid = spheroid;

    spheroid_axes_lengths(spheroid,
        &mg->re_major, &mg->re_minor);

    mp->re_major = mg->re_major;
    mp->re_minor = mg->re_minor;

    // location block
    ml->lon_start_near_range = mp->startX;
    ml->lat_start_near_range = mp->startY;
    ml->lon_start_far_range = mp->startX + mp->perX * ncols;
    ml->lat_start_far_range = mp->startY;
    ml->lon_end_near_range = mp->startX;
    ml->lat_end_near_range = mp->startY + mp->perY * nrows;
    ml->lon_end_far_range = mp->startX + mp->perX * ncols;
    ml->lat_end_far_range = mp->startY + mp->perY * nrows;

    free(hdr_file);
    free(prj_file);

    *column_count = ncols;
    *row_count = nrows;
    
    return meta;
}
Example #4
0
int
geocode_dem (projection_type_t projection_type,	// What we are projection to.
	     project_parameters_t *pp,    // Parameters we project to.
	     datum_type_t datum,                // Datum we project to.
	     // Pixel size of output image, in output projection units
	     // (meters or possibly degrees, if we decide to support
	     // projecting to pseudoprojected form).
	     double pixel_size,
	     resample_method_t resample_method,	// How to resample pixels.
	     const char *input_image, // Base name of input image.
	     const meta_parameters *imd, // Input DEM image metadata.
	     const char *output_image  // Base name of output image.
	     )
{
  int return_code;		// Holds return codes from functions.

  // Function to use to project or unproject between latlon and input
  // or output coordinates.
  projector_t project_input; 	// latlon => input image map projection
  projector_t unproject_input;	// input image_map_projection => latlon
  projector_t project_output;	// latlon => output image map projection
  projector_t unproject_output;	// output image map projection => latlon
  // Like the above, but act on arrays.
  array_projector_t array_project_input, array_unproject_input;
  array_projector_t array_project_output, array_unproject_output;

  // We only deal with reprojection map projected DEMs.
  g_assert (imd->projection != NULL);

  // FIXME: what to do with background value is something that still
  // needs to be determined (probably in consultation with the guys
  // working on terrain correction).
  const float background_value = 0.0;

  // Geocoding to pseudoprojected form presents issues, for example
  // with the meaning of the pixel_size argument, which is taken as a
  // distance in map projection coordinates for all other projections
  // (deciding how to interpret it when projecting to pseudoprojected
  // form is tough), and since there probably isn't much need, we
  // don't allow it.
  g_assert (projection_type != LAT_LONG_PSEUDO_PROJECTION);

  // Get the functions we want to use for projecting and unprojecting.
  set_projection_functions (imd->projection->type, &project_input,
			    &unproject_input, &array_project_input,
			    &array_unproject_input);
  set_projection_functions (projection_type, &project_output,
			    &unproject_output, &array_project_output,
			    &array_unproject_output);

  // Input image dimensions in pixels in x and y directions.
  size_t ii_size_x = imd->general->sample_count;
  size_t ii_size_y = imd->general->line_count;

  // Convenience aliases.
  meta_projection *ipb = imd->projection;
  project_parameters_t *ipp = &imd->projection->param;

  // First we march around the entire outside of the image and compute
  // projection coordinates for every pixel, keeping track of the
  // minimum and maximum projection coordinates in each dimension.
  // This lets us determine the exact extent of the DEM in
  // output projection coordinates.
  asfPrintStatus ("Determining input image extent in projection coordinate "
		  "space... ");

  double min_x = DBL_MAX;
  double max_x = -DBL_MAX;
  double min_y = DBL_MAX;
  double max_y = -DBL_MAX;

  // In going around the edge, we are just trying to determine the
  // extent of the image in the horizontal, so we don't care about
  // height yet.
  { // Scoping block.
    // Number of pixels in the edge of the image.
    size_t edge_point_count = 2 * ii_size_x + 2 * ii_size_y - 4;
    double *lats = g_new0 (double, edge_point_count);
    double *lons = g_new0 (double, edge_point_count);
    size_t current_edge_point = 0;
    size_t ii = 0, jj = 0;
    for ( ; ii < ii_size_x - 1 ; ii++ ) {
      return_code = get_pixel_lat_long (imd, unproject_input, ii, jj,
					&(lats[current_edge_point]),
					&(lons[current_edge_point]));
      g_assert (return_code);
      current_edge_point++;
    }
    for ( ; jj < ii_size_y - 1 ; jj++ ) {
      return_code = get_pixel_lat_long (imd, unproject_input, ii, jj,
					&(lats[current_edge_point]),
					&(lons[current_edge_point]));
      g_assert (return_code);
      current_edge_point++;
    }
    for ( ; ii > 0 ; ii-- ) {
      return_code = get_pixel_lat_long (imd, unproject_input, ii, jj,
					&(lats[current_edge_point]),
					&(lons[current_edge_point]));
      g_assert (return_code);
      current_edge_point++;
    }
    for ( ; jj > 0 ; jj-- ) {
      return_code = get_pixel_lat_long (imd, unproject_input, ii, jj,
					&(lats[current_edge_point]),
					&(lons[current_edge_point]));
      g_assert (return_code);
      current_edge_point++;
    }
    g_assert (current_edge_point == edge_point_count);
    // Pointers to arrays of projected coordinates to be filled in.
    // The projection function will allocate this memory itself.
    double *x = NULL, *y = NULL;
    // Project all the edge pixels.
    return_code = array_project_output (pp, lats, lons, NULL, &x, &y, NULL,
					edge_point_count, datum);
    g_assert (return_code == TRUE);
    // Find the extents of the image in projection coordinates.
    for ( ii = 0 ; ii < edge_point_count ; ii++ ) {
      if ( x[ii] < min_x ) { min_x = x[ii]; }
      if ( x[ii] > max_x ) { max_x = x[ii]; }
      if ( y[ii] < min_y ) { min_y = y[ii]; }
      if ( y[ii] > max_y ) { max_y = y[ii]; }
    }

    free (y);
    free (x);
    g_free (lons);
    g_free (lats);
  }

  asfPrintStatus ("done.\n\n");

  // Issue a warning when the chosen pixel size is smaller than the
  // input pixel size.  FIXME: this condition will really never fire
  // for pseudoprojected image, since the pixels size of the input is
  // tiny (degrees per pixel) and the pixel_size has already been
  // computed in asf_geocode function itself as an arc length on the
  // ground.
  if ( GSL_MIN(imd->general->x_pixel_size,
	       imd->general->y_pixel_size) > pixel_size ) {
    asfPrintWarning
      ("Requested pixel size %f is smaller then the input image resolution "
       "(%le meters).\n", pixel_size,
       GSL_MIN (imd->general->x_pixel_size, imd->general->y_pixel_size));
  }

  // The pixel size requested by the user better not oversample by the
  // factor of 2.  Specifying --force will skip this check.  FIXME:
  // same essential problem as the above condition, but in this case
  // it always goes off.
  //  if (!force_flag && GSL_MIN(imd->general->x_pixel_size,
  //	       imd->general->y_pixel_size) > (2*pixel_size) ) {
  //    report_func
  //      ("Requested pixel size %f is smaller then the minimum implied by half \n"
  //       "the input image resolution (%le meters), this is not supported.\n",
  //       pixel_size, GSL_MIN (imd->general->x_pixel_size,
  //			    imd->general->y_pixel_size));
  //  }

  asfPrintStatus ("Opening input DEM image... ");
  char *input_data_file = (char *) MALLOC(sizeof(char)*(strlen(input_image)+5));
  sprintf(input_data_file, "%s.img", input_image);
  FloatImage *iim
    = float_image_new_from_file (ii_size_x, ii_size_y, input_data_file, 0,
				 FLOAT_IMAGE_BYTE_ORDER_BIG_ENDIAN);
  FREE(input_data_file);
  asfPrintStatus ("done.\n\n");

  // Maximum pixel indicies in output image.
  size_t oix_max = ceil ((max_x - min_x) / pixel_size);
  size_t oiy_max = ceil ((max_y - min_y) / pixel_size);

  // Output image dimensions.
  size_t oi_size_x = oix_max + 1;
  size_t oi_size_y = oiy_max + 1;

  // Output image.
  FloatImage *oim = float_image_new (oi_size_x, oi_size_y);

  // Translate the command line notion of the resampling method into
  // the lingo known by the float_image class.  The compiler is
  // reassured with a default.
  float_image_sample_method_t float_image_sample_method
    = FLOAT_IMAGE_SAMPLE_METHOD_BILINEAR;
  switch ( resample_method ) {
  case RESAMPLE_NEAREST_NEIGHBOR:
    float_image_sample_method = FLOAT_IMAGE_SAMPLE_METHOD_NEAREST_NEIGHBOR;
    break;
  case RESAMPLE_BILINEAR:
    float_image_sample_method = FLOAT_IMAGE_SAMPLE_METHOD_BILINEAR;
    break;
  case RESAMPLE_BICUBIC:
    float_image_sample_method = FLOAT_IMAGE_SAMPLE_METHOD_BICUBIC;
    break;
  default:
    g_assert_not_reached ();
  }

  // We need to find the z coordinates in the output projection of all
  // the pixels in the input DEM.  We store these values in their own
  // FloatImage instance.

  //FloatImage *x_coords = float_image_new (ii_size_x, ii_size_y);
  //FloatImage *y_coords = float_image_new (ii_size_x, ii_size_y);
  FloatImage *z_coords = float_image_new (ii_size_x, ii_size_y);

  // We transform the points using the array transformation function
  // for efficiency, but we don't want to do them all at once, since
  // that would require huge gobs of memory.
  const size_t max_transform_chunk_pixels = 5000000;
  size_t rows_per_chunk = max_transform_chunk_pixels / ii_size_x;
  size_t chunk_pixels = rows_per_chunk * ii_size_x;
  double *chunk_x = g_new0 (double, chunk_pixels);
  double *chunk_y = g_new0 (double, chunk_pixels);
  double *chunk_z = g_new0 (double, chunk_pixels);
  double *lat = g_new0 (double, chunk_pixels);
  double *lon = g_new0 (double, chunk_pixels);
  double *height = g_new0 (double, chunk_pixels);

  asfPrintStatus ("Determining Z coordinates of input pixels in output "
		  "projection space... ");

  // Transform all the chunks, storing results in the z coordinate image.
  size_t ii, jj, kk;		// Index variables.
  for ( ii = 0 ; ii < ii_size_y ; ) {
    size_t rows_remaining = ii_size_y - ii;
    size_t rows_to_load
      = rows_per_chunk < rows_remaining ? rows_per_chunk : rows_remaining;
    for ( jj = 0 ; jj < rows_to_load ; jj++ ) {
      size_t current_image_row = ii + jj;
      for ( kk = 0 ; kk < ii_size_x ; kk++ ) {
	size_t current_chunk_pixel = jj * ii_size_x + kk;
	chunk_x[current_chunk_pixel] = ipb->startX + kk * ipb->perX;
	chunk_y[current_chunk_pixel]
	  = ipb->startY + current_image_row * ipb->perY;
	if ( imd->projection->type == LAT_LONG_PSEUDO_PROJECTION ) {
	  chunk_x[current_chunk_pixel] *= D2R;
	  chunk_y[current_chunk_pixel] *= D2R;
	}
	chunk_z[current_chunk_pixel]
	  = float_image_get_pixel (iim, kk, current_image_row);
      }
    }
    long current_chunk_pixels = rows_to_load * ii_size_x;
    array_unproject_input (ipp, chunk_x, chunk_y, chunk_z, &lat, &lon,
			   &height, current_chunk_pixels, ipb->datum);
    array_project_output (pp, lat, lon, height, &chunk_x, &chunk_y, &chunk_z,
			  current_chunk_pixels, datum);
    for ( jj = 0 ; jj < rows_to_load ; jj++ ) {
      size_t current_image_row = ii + jj;
      for ( kk = 0 ; kk < ii_size_x ; kk++ ) {
	size_t current_chunk_pixel = jj * ii_size_x + kk;
	// Current pixel x, y, z coordinates.
	//float cp_x = (float) chunk_x[current_chunk_pixel];
	//float cp_y = (float) chunk_y[current_chunk_pixel];
	float cp_z = (float) chunk_z[current_chunk_pixel];
	//float_image_set_pixel (x_coords, kk, current_image_row, cp_x);
	//float_image_set_pixel (y_coords, kk, current_image_row, cp_y);
	float_image_set_pixel (z_coords, kk, current_image_row, cp_z);
      }
    }

    ii += rows_to_load;
  }

  asfPrintStatus ("done.\n\n");

#ifdef DEBUG_GEOCODE_DEM_Z_COORDS_IMAGE_AS_JPEG
  // Take a look at the z_coordinate image (for debugging).
  float_image_export_as_jpeg_with_mask_interval (z_coords, "z_coords.jpg",
						 GSL_MAX (z_coords->size_x,
							  z_coords->size_y),
						 -FLT_MAX, -100);
#endif

  g_free (chunk_x);
  g_free (chunk_y);
  g_free (chunk_z);
  g_free (lat);
  g_free (lon);
  g_free (height);

  // Now we want to determine the pixel coordinates in the input which
  // correspond to each of the output pixels.  We can then sample the
  // new height value already computed for that input pixel to
  // determine the pixel value to use as output.

  // We want to proceed in chunks as we did when going in the other
  // direction.
  rows_per_chunk = max_transform_chunk_pixels / oi_size_x;
  chunk_pixels = rows_per_chunk * oi_size_x;
  chunk_x = g_new0 (double, chunk_pixels);
  chunk_y = g_new0 (double, chunk_pixels);
  // We don't have height information in this direction, nor do we care.
  chunk_z = NULL;
  lat = g_new0 (double, chunk_pixels);
  lon = g_new0 (double, chunk_pixels);
  // We don't have height information in this direction, nor do we care.
  height = NULL;

  asfPrintStatus ("Sampling Z coordinates to form pixels in output projection "
		  "space... ");

  // Transform all the chunks, using the results to form the output image.
  for ( ii = 0 ; ii < oi_size_y ; ) {
    size_t rows_remaining = oi_size_y - ii;
    size_t rows_to_load
      = rows_per_chunk < rows_remaining ? rows_per_chunk : rows_remaining;
    for ( jj = 0 ; jj < rows_to_load ; jj++ ) {
      size_t current_image_row = ii + jj;
      for ( kk = 0 ; kk < oi_size_x ; kk++ ) {
	size_t current_chunk_pixel = jj * oi_size_x + kk;
	chunk_x[current_chunk_pixel] = min_x + kk * pixel_size;
	chunk_y[current_chunk_pixel] = max_y - current_image_row * pixel_size;
      }
    }
    long current_chunk_pixels = rows_to_load * oi_size_x;
    array_unproject_output (pp, chunk_x, chunk_y, NULL, &lat, &lon, NULL,
			    current_chunk_pixels, datum);
    array_project_input (ipp, lat, lon, NULL, &chunk_x, &chunk_y, NULL,
			 current_chunk_pixels, ipb->datum);
    if ( imd->projection->type == LAT_LONG_PSEUDO_PROJECTION ) {
      ssize_t ll;     // For (semi)clarity we don't reuse index variable :)
      for ( ll = 0 ; ll < current_chunk_pixels ; ll++ ) {
	chunk_x[ll] *= R2D;
	chunk_y[ll] *= R2D;
      }
    }

    for ( jj = 0 ; jj < rows_to_load ; jj++ ) {
      size_t current_image_row = ii + jj;
      for ( kk = 0 ; kk < oi_size_x ; kk++ ) {
	size_t current_chunk_pixel = jj * oi_size_x + kk;

	// Compute pixel coordinates in input image.
	ssize_t in_x
	  = (chunk_x[current_chunk_pixel] - ipb->startX) / ipb->perX;
	ssize_t in_y
	  = (chunk_y[current_chunk_pixel] - ipb->startY) / ipb->perY;

	if ( in_image (z_coords, in_x, in_y) ) {
	  // FIXME: something needs to be done somewhere about
	  // propogating no data values.
	  float_image_set_pixel (oim, kk, current_image_row,
				 float_image_sample (z_coords, in_x, in_y,
						     resample_method));
	}
	else {
	  float_image_set_pixel (oim, kk, current_image_row, background_value);
	}
      }
    }

    ii += rows_to_load;
  }

  asfPrintStatus ("done.\n\n");

  g_free (chunk_x);
  g_free (chunk_y);
  g_free (lat);
  g_free (lon);

#ifdef DEBUG_GEOCODE_DEM_OUTPUT_IMAGE_AS_JPEG
  // Take a look at the output image (for debugging).
  float_image_export_as_jpeg_with_mask_interval (oim, "oim.jpg",
						 GSL_MAX (oim->size_x,
							  oim->size_y),
						 -FLT_MAX, -100);
#endif

  // Store the output image.
  asfPrintStatus ("Storing output image... ");
  char *output_data_file = 
    (char *) MALLOC(sizeof(char)*(strlen(output_image)+5));
  sprintf(output_data_file, "%s.img", output_image);
  return_code = float_image_store (oim, output_data_file,
				   FLOAT_IMAGE_BYTE_ORDER_BIG_ENDIAN);
  g_assert (return_code == 0);
  asfPrintStatus ("done.\n\n");

  // Now we need some metadata for the output image.  We will just
  // start with the metadata from the input image and add the
  // geocoding parameters.

  char *input_meta_file = (char *) MALLOC(sizeof(char)*(strlen(input_image)+6));
  sprintf(input_meta_file, "%s.meta", input_image);

  char *output_meta_file = 
    (char *) MALLOC(sizeof(char)*(strlen(output_image)+6));
  sprintf(output_meta_file, "%s.meta", output_image);

  meta_parameters *omd = meta_read (input_meta_file);

  // Adjust the metadata to correspond to the output image instead of
  // the input image.

  omd->general->x_pixel_size = pixel_size;
  omd->general->y_pixel_size = pixel_size;
  omd->general->line_count = oi_size_y;
  omd->general->sample_count = oi_size_x;

  // SAR block is not really appropriate for map projected images, but
  // since it ended up with this value that can signify map
  // projectedness in it somehow, we fill it in for safety.
  omd->sar->image_type = 'P';

  // Note that we have already verified that the input image is
  // projected, and since we initialize the output metadata from there
  // we know we will have a projection block.
  omd->projection->type = projection_type;
  omd->projection->startX = min_x;
  omd->projection->startY = max_y;
  omd->projection->perX = pixel_size;
  omd->projection->perY = -pixel_size;
  strcpy (omd->projection->units, "meters");

  // Set the spheroid axes lengths as appropriate for the output datum.
  spheroid_axes_lengths (datum_spheroid (datum), &(omd->projection->re_major),
			 &(omd->projection->re_minor));

  // What the heck, might as well set the ones in the general block as
  // well.
  spheroid_axes_lengths (datum_spheroid (datum), &(omd->general->re_major),
			 &(omd->general->re_minor));

  // Latitude and longitude at center of the output image.  We will
  // set these relative to the spheroid underlying the datum in use
  // for the projected image.  Yeah, that seems appropriate.
  double lat_0, lon_0;
  double center_x = omd->projection->startX + (omd->projection->perX
					       * omd->general->line_count / 2);
  double center_y = (omd->projection->startY
		     + (omd->projection->perY
			* omd->general->sample_count / 2));
  unproject_output (pp, center_x, center_y, ASF_PROJ_NO_HEIGHT, &lat_0, &lon_0,
		    NULL, datum);
  omd->general->center_latitude = R2D * lat_0;
  omd->general->center_longitude = R2D * lon_0;

  // FIXME: We are ignoring the meta_location fields for now since I'm
  // not sure whether they are supposed to refer to the corner pixels
  // or the corners of the data itself.

  if ( lat_0 > 0.0 ) {
    omd->projection->hem = 'N';
  }
  else {
    omd->projection->hem = 'S';
  }

  // Convert the projection parameter values back into degrees.
  to_degrees (projection_type, pp);
  omd->projection->param = *pp;
  meta_write (omd, output_meta_file);

  float_image_free (oim);
  FREE(output_data_file);
  meta_free (omd);
  FREE(input_meta_file);
  FREE(output_meta_file);

  return 0;
}