
/****************************************************************
*                                                               *
* dem2rad.c	version 0.2					*
* (type dem2rad help for usage)                                 *
*                                                               *
* This program converts a Digital Elevation Models (DEM) file   *
*   to a coordinate file that the gensurf command of Radiance   *
*   can utilize.                                                *
*   --- coordinate triplets                                     *
*								*
* This program is written particularly for 7.5' dem files       *
*    and I didn't bother to look into the 1-degree dem file     *
*    format enough to tell if it will work for that.  At least  *
*    it will give you warning messages (if you don't give the   *
*    -m switch).                                                *
*								*
* The only 7.5' elevation data that will work are the files     *
*    saved as old-style DEMs. The new-style SDTS files now      *
*    available for most of the US at the URL below will not     *
*    work. There are some old-style 7.5' DEMs at:		*
*    ftp://spectrum.xerox.com/pub/map/dem/			*
*								*
* It now works for 1-degree DEMs, found for the entire US at    *
*    http://edcwww.cr.usgs.gov/doc/edchome/ndcdb/ndcdb.html     *
*                                                               *
* Author : Gilbert Leung    gleung@athena.mit.edu               *
*   modified from the program demex.c                           *
*   (Author : Robert Smyser?)                                   *
*   ---fixed quite a few of fortran i/o bugs                    *
* Other modifications by Mark Stock, 1998-05-05, include:       *
*     -ANSI C compliance (not complete, enough to run)          *
*     -changed RECLEN from 1024 to 1025 b/c scanf behavior      *
*     -added some more debugging output to stderr               *
*     -added subsampling option                                 *
*     -added centering option                                   *
*     -added vertical exaggeration                              *
*     -allowed reading of the header (no file writing)          *
*     -fixed problem with incomplete columns                    *
*     -longitudinal scaling automatic on 1-degree DEMs          *
*                                                               *
* Any questions or comments, please e-mail mstock@umich.edu     *
*                                                               *
* Fixes to allow compilation on current compilers by            *
* Lars O. Grobe, 2011-12-21, include:                           *
*     -include <stdlib.h> for function exit()                   *
*     -replace <strings.h> by <string.h> for portability        *
*     -replace handles stdin, stdout by _IO_stdin, _IO_stdout   *
*     -main() returns 0 on success                              *
*     -removed unused variables                                 *
* Any questions or comments, please e-mail grobe@gmx.net        *
*                                                               *
*****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "proto.h"

#define RECLEN   1025           /* record length */
#define DEMWIDTH 1201
#define DEMHEIGHT 1201

char line[RECLEN + 1];
char *bufptr, *buf_end;

#define MIN(a,b)   ((a) < (b) ? (a) : (b))
#define MAX(a,b)   ((a) > (b) ? (a) : (b))

static char *progName;
/* static FILE *ofp = stdout, *ifp = stdin, *popen(), *fopen(); */
static FILE *ofp = _IO_stdout, *ifp = _IO_stdin; /* log: added _IO_ */
extern FILE *popen(), *fopen();
static float x_res, y_res, z_res, old_x_res, old_y_res;
static double shift_left, shift_down;
static double file_south, file_west;
static int xoffset = 0, yoffset = 0;
static int subsample = 1;
static int warnflag = 1;
static int width = 0, height = 0;
static int total_cols;
static int center_it = 1;               /* center the output? 0=no, 1=yes */
static int vertical = 1.0;              /* vertical exaggeration */
static int have_infile = 0;             /* does an input file exist? 0=no, 1=yes */
static int ref_sys = 1;                 /* 0 for 1 degree DEMs, 1 or 2 for 7.5" series */
int debug = 0;				/* write debugging statements? 0=no, 1=yes */


static int DoProfile(int maxRows)
{
    /* Reads a column from the DEM */
    int i, tmp, rowoffset;
    int rowId, colId, nelev, elev;
    int pts = 0;
    double xcoord, ycoord, datum;
    double tmpd;

    /* for 1-deg DEMs, skip one char */
    if (ref_sys == 0) bufptr++;

    GetInt(&rowId);
    GetInt(&colId);
    GetInt(&nelev);
    GetInt(&tmp);
    GetDouble(&xcoord);
    GetDouble(&ycoord);
    GetDouble(&datum);
    GetDouble(&tmpd);
    GetDouble(&tmpd);

    /* read in the whole profile even we need only part of it */
    if (debug) fprintf(stderr,"need to read %d rows of %d, S to N,",height,nelev);

    /* determine row offset if the data begins some rows up from the lower edge! */
    rowoffset = (int)(ycoord - file_south)/old_y_res;
    if (rowoffset < subsample/4) rowoffset = 0;		/* an arbitrary threshold */
    if (rowoffset > 0) {
        /* fprintf(stderr,"  data starts at row %d, starting with %d zero-height points.\n",rowoffset,rowoffset/subsample+1); */
        for (i = 0; i <= rowoffset/subsample; i++) {
            if (i*subsample >= yoffset) {
                fprintf(ofp, "%9.2lf %9.2lf      0\t", (xcoord-shift_left)*x_res/old_x_res, ycoord - (rowoffset/subsample-i+0.75)*y_res*subsample - shift_down);
                pts++;
            }
        }
    }

    /* read the data in that column */
    for (i = 0; i < nelev; i++) {
        GetInt(&elev);
        /* fprintf(stderr,"  read an elevation of (%6d)\n",elev); */
        if (i+rowoffset >= yoffset && i+rowoffset < maxRows && (i+rowoffset-yoffset)%subsample == 0) {
            if (center_it)
                fprintf(ofp, "%9.2lf %9.2lf %6d\t", (xcoord-shift_left)*x_res/old_x_res, ycoord + i*y_res - shift_down, elev*vertical);
            else
                fprintf(ofp, "%12.2lf %12.2lf %6d\t", xcoord*x_res/old_x_res, (ycoord + i*old_y_res)*y_res, elev*vertical);
            pts++;
        }
    }

    /* fill in zeros for the remainder if there aren't enough rows */
    for (i += rowoffset ; i < maxRows; i+=subsample) {
        if (center_it)
            fprintf(ofp, "%12.2lf %12.2lf      0\t", (xcoord-shift_left)*x_res/old_x_res, ycoord + i*y_res - shift_down); 
        else
            fprintf(ofp, "%12.2lf %12.2lf      0\t", xcoord*x_res/old_x_res, (ycoord + i*old_y_res)*y_res);
        pts++;
    }

    if (fprintf(ofp, "\n") < 0) {
        fprintf(stderr, "can't write output\n");
        exit(4);
    }

    return pts;
}



/* Process header, preserve useful info in global vars */
/* x_res, y_res, z_res, and total_cols */

static void GetDemHeader(int echo_info)
{
    char name[51];
    /* double file_north, file_south, file_east, file_west; */
    double file_north, file_east;
    double max_elev, min_elev;
    int i, sides, z_unit, xy_unit, P_rows;
    int level_code, elev_pattern, accuracy;
    int ref_zone;
    double ddummy, angle, lng[4], lat[4];
    /* double xscale,yscale; */ /* log: not used */

    GetBuffer();
    bcopy(bufptr, name, 50);
    bufptr += 144;

    GetInt(&level_code);
    GetInt(&elev_pattern);
    GetInt(&ref_sys);
    GetInt(&ref_zone);

    if (warnflag) {
        if (level_code == 0)
            fprintf(stderr, "Not 7.5 minute dem, level_code is %d, not 1.\n",level_code);
        if (elev_pattern != 1)
            fprintf(stderr, "Non-regular pattern, elev_pattern is %d, not 1.\n",elev_pattern);
        if (ref_sys != 1)
            fprintf(stderr, "Non-UTM coordinate system, ref_sys is %d, not 1.\n",ref_sys);
        if (ref_zone != 10)
            fprintf(stderr, "Non-UTM zone, ref_zone is %d, not 10.\n",ref_zone);
    }
    for (i = 0; i < 15; i++)
        GetDouble(&ddummy);

    GetInt(&xy_unit);
    GetInt(&z_unit);
    GetInt(&sides);

    for (i = 0; i < sides; i++) {
        GetDouble(&lng[i]);
        GetDouble(&lat[i]);
    }
    GetDouble(&min_elev);
    GetDouble(&max_elev);
    GetDouble(&angle);          /* element 12 */
    if (warnflag && angle != 0.0)
        fprintf(stderr, "Non-zero angle\n");

    /* accuracy code */
    GetInt(&accuracy);

    GetFloat(&x_res);
    GetFloat(&y_res);
    GetFloat(&z_res);
    if (!x_res) {
        x_res = y_res;
        if (warnflag)
            fprintf(stderr, "zero x-resolution (set to y-resolution)\n");
    }
    if (!y_res) {
        y_res = x_res;
        if (warnflag)
            fprintf(stderr, "zero y-resolution (set to x-resolution)\n");
        if (warnflag && !x_res)
            fprintf(stderr, "Both x-res and y-res are zero\n");
    }
    GetInt(&P_rows);
    GetInt(&total_cols);

    /* if its a 1-degree DEM, skip up to the end of the header */
    if (ref_sys == 0) bufptr += 121;

    /* calcluate the boundaries */
    if (echo_info != 0) {
    file_north = -9999999.0;
    file_south = 9999999.0;
    file_east = -9999999.0;
    file_west = 9999999.0;
    for (i = 0; i < sides; i++) {
        file_north = MAX(lat[i], file_north);
        file_south = MIN(lat[i], file_south);
        file_east = MAX(lng[i], file_east);
        file_west = MIN(lng[i], file_west);
    }

    /* do some processing on xy_unit for scale purposes */
    fprintf(stderr, "Old: x_res %g  y_res %g  z_res %g\n", x_res, y_res, z_res);
    old_x_res = x_res;  /* save this for centering calculation */
    old_y_res = y_res;
    if (xy_unit == 0) {
        /* units are radians */
        x_res *= 25515720.5 * cos((file_north+file_south)/2.0);
        y_res *= 25515720.5;
    } else if (xy_unit == 1) {
        /* units are feet */
        x_res *= 3.2808399;
        y_res *= 3.2808399;
    } else if (xy_unit == 2) {
        /* units are meters */
        /* do nothing */
    } else {
        /* units are arcseconds */
        /* find latitude, then calculate xscale */
        /* 42406841E-06 is pi/2 / (90*60*60) / 2 */
        x_res *= 31 * cos((file_north+file_south)*2.42406841E-06);
        y_res *= 31;
    }

    /* Write out the header information */
    fprintf(stderr, "\nname = (%s)\n", name);
    fprintf(stderr, "level_code %d  elev_pattern %d  ref_sys %d  ref_zone %d\n",
            level_code, elev_pattern, ref_sys, ref_zone);
    fprintf(stderr, "plan units: %s  ", (xy_unit == 0 ? "radians" :
                    (xy_unit == 1 ? "feet" :
                            (xy_unit == 2 ? "meters" :
                                    "arc-seconds"))));
    fprintf(stderr, "elev units: %s\n", z_unit == 1 ? "feet" : "meters");
    fprintf(stderr, "number of sides  %d\n", sides);
    fprintf(stderr, "min_elev %g  max_elev %g  angle %g\n",
            min_elev, max_elev, angle);
    fprintf(stderr, "accuracy code %d\n", accuracy);
    fprintf(stderr, "x_res %g  y_res %g  z_res %g\n",
            x_res, y_res, z_res);
    fprintf(stderr, "Farthest extent of data:\n");
    fprintf(stderr, "file_north %lf  file_south %lf\n",
            file_north, file_south);
    fprintf(stderr, "file_west %lf  file_east %lf\n",
            file_west, file_east);
    fprintf(stderr, "number of columns (W->E) %d\n\n", total_cols);
    }
}


void GetBuffer()
{                               /* one string-line */
    if (fgets(line, RECLEN, ifp) == NULL) {
        fprintf(stderr, "GetBuffer: error reading file\n");
        exit(4);
    }
    bufptr = line;
    buf_end = line + strlen(line);
    /* fprintf(stderr, "Getting a new buffer; buf_end-bufptr = %d\n",buf_end-bufptr); */
    /* if a 1-deg DEM, advance 2 chars */
    /* fprintf(stderr, "Here's the buffer: (%s)\n",line); */

}



static void Usage(int status)       /* help message */
{
    static char **cpp, *help_message[] =
    {
            "where options include:",
            "  -in <file>       input dem filename",
            "  -out <file>      output radiance gensurf filename",
            "  -xoffset <val>   offset in X direction (starts from 0)",
            "  -yoffset <val>   offset in Y direction (starts from 0)",
            "  -width  <val>    number of columns to extract",
            "  -height <val>    number of rows to extract",
            "  -subsample <val> read every nth value (1 means use full resolution",
            "                   2 means use every 2nd value in both dimensions)", 
            "  -vertical <val>  use vertical exaggeration factor (integer, default is 1)",
            "  -nocenter        do not center the output on the origin",
            "  -getinfo         read the header, echo useful information, and exit",
            "  -m               turn off warning messages",
            "  -debug           turn on debugging messages",
            " ",
            "Options may be abbreviated to an unambiguous length.",
            "stdin, stdout are used if no -in -out are specified.",
            "Last note: when subsampling is used, width and height are the",
            "  dimensions of the output. For example, if subsample is set to",
            "  5 and width is 11, the columns to be extracted will include",
            "  columns 0,5,10,15...45, and 50. Be careful not to exceed the",
            "  size of the DEM. A warning will appear if that would occur."
            "\n",
            NULL
    };

    fprintf(stderr, "usage:\n  %s [-options ...]\n\n", progName);
    for (cpp = help_message; *cpp; cpp++)
        fprintf(stderr, "%s\n", *cpp);
    exit(status);
}



int main(int argc,char *argv[])
{
    int i, j, k;
    int pclose(), fclose(), (*closefunc)() = fclose;
    int dummy, rowswrote;
    int col, nelev;
    /* double xslide,yslide; */ /* log: unused variables */
    char *outfile = NULL;
    /* char temp[121]; */

    progName = argv[0];
    if (argc < 2) Usage(1);
    for (i = 1; i < argc; i++) {
        if (strncmp(argv[i], "-in", 2) == 0) {
            char *ptr = rindex(argv[++i], '.');
            if (ptr && strcmp(ptr, ".Z") == 0) {
                char popen_cmd[80];

                (void) strcpy(popen_cmd, "zcat ");
                (void) strcat(popen_cmd, argv[i]);
                ifp = popen(popen_cmd, "r");
                closefunc = pclose;
            } else
                ifp = fopen(argv[i], "r");
            if (ifp == NULL) {
                fprintf(stderr, "can't open input file: %s ", argv[i]);
                exit(1);
            }
            have_infile = 1;            /* an infile exists */
        } else if (strncmp(argv[i], "-out", 2) == 0) {
            if ((ofp = fopen(argv[++i], "w")) == NULL) {
                fprintf(stderr, "can't open output file: %s ", argv[i]);
                exit(1);
            }
            outfile = argv[i];
        } else if (strncmp(argv[i], "-xoffset", 2) == 0)
            xoffset = atoi(argv[++i]);
        else if (strncmp(argv[i], "-yoffset", 2) == 0)
            yoffset = atoi(argv[++i]);
        else if (strncmp(argv[i], "-width", 2) == 0)
            width = atoi(argv[++i]);
        else if (strncmp(argv[i], "-height", 2) == 0)
            height = atoi(argv[++i]);
        else if (strncmp(argv[i], "-subsample", 2) == 0)
            subsample = atoi(argv[++i]);
        else if (strncmp(argv[i], "-vertical", 2) == 0)
            vertical = atoi(argv[++i]);
        else if (strncmp(argv[i], "-nocenter", 2) == 0)
            center_it = 0;
        else if (strncmp(argv[i], "-getinfo", 2) == 0) {
            if (have_infile) {
                GetDemHeader(1); 
                /* find out how many rows exist */
                if (ref_sys != 0) {
                    /* its a 7.5' DEM */
                    for (j = 0 ; j < 6 ; j++) {
                        GetBuffer();
                        /* Don't save anything from this column */
                        GetInt(&dummy);
                        GetInt(&dummy);
                        GetInt(&nelev);
                        GetInt(&dummy);
                        bufptr += 5 * 24;       /* 5 doubles */
                        for (k = 0; k < nelev; k++)
                            GetInt(&dummy);
                    }
                } else {
                    /* its a 1-degree DEM */
                    /* bcopy(bufptr,temp,120); */
                    /* fprintf(stderr, "%d(%s)\n",bufptr,temp); */
                    /* bufptr += 120; */
                    GetBuffer();
                    bufptr++;
                    GetInt(&dummy);
                    GetInt(&dummy);
                    GetInt(&nelev);
                }
                fprintf(stderr, "number of rows (N->S) %d\n\n", nelev);
                exit(4);
            } else {            /* if infile does not exist yet */
                fprintf(stderr, "\nNo input file exists yet. Place -in infile.dem[.Z]\n");
                fprintf(stderr, "   before -getinfo in the command line.\n\n");
                exit(4);
            }
        } else if (strcmp(argv[i], "-m") == 0)
            warnflag = 0;
        else if (strncmp(argv[i], "-debug", 2) == 0)
            debug = 1;
        else
            Usage(1);
    }

    if ((xoffset < 0) || (yoffset < 0) || (width < 0) || (height < 0)) {
        fprintf(stderr, "Bad argument passed. Use positive integers for xoffset, yoffset, width, and height.\n");
        Usage(2);
    }

    GetDemHeader(1);

    if (width == 0) {
        if (warnflag) fprintf(stderr, "Width undefined, using maximum allowed.\n");
        width = (total_cols - xoffset - 1)/subsample + 1;
        if (warnflag) fprintf(stderr, "   Making width = %d\n",width);
    }
    if (height == 0) {
        if (warnflag) fprintf(stderr, "Height undefined, setting it equal to width.\n");
        height = width;
        if (warnflag) fprintf(stderr, "   Making height = %d\n",height);
        if (ref_sys != 0 && warnflag) fprintf(stderr, "   Warning: You may lose a strip of data above this threshold.\n\n");
    }

    if (center_it) {
        shift_left = file_west + old_x_res*xoffset + old_x_res*(width-1)*subsample/2;
        shift_down = file_south + y_res*yoffset + y_res*(height-1)*subsample/2;
    }

    /* Look at all columns up to & including the last one neccessary */
    for (j = 0; j < xoffset+(width-1)*subsample+1; j++) {
        if (j >= xoffset && (j-xoffset)%subsample == 0 ) {

            /* Read and save values from this column */
            GetBuffer();
            if (j+1 > total_cols) {
                width = (total_cols - xoffset + 1)/subsample;
                if (warnflag)
                    fprintf(stderr, "Not enough columns.. file is truncated to a width of %d\n", width);
                break;
            }
            if (debug)
                fprintf(stderr, "Reading column %d, ",j);
            else {
                fprintf(stderr, ".");
                fflush(stderr);
            }
            rowswrote = DoProfile(yoffset + (height-1)*subsample + 1);
            if (debug) fprintf(stderr, " and wrote %d rows.\n",rowswrote);

        } else {

            /* Don't save anything from this column */
            GetBuffer();
            GetInt(&dummy);
            GetInt(&col);
            /* fprintf(stderr, "   col = %d, and j = %d\n",col,j); */
            GetInt(&nelev);
            GetInt(&dummy);
            bufptr += 5 * 24;       /* 5 doubles */
            for (i = 0; i < nelev; i++)
                GetInt(&dummy);
        }
    }

    (*closefunc)(ifp);

    if (!debug) fprintf(stderr, "\n");
    fprintf(stderr, "\nDEM file extracted successfully.\n");

    if (ofp != stdout)
        fprintf(stderr, "\nUse the command:\ngensurf mat name %s %s %s %d %d\n",
            outfile, outfile, outfile, height, width);
    fclose(ofp);
    return (0);
}

/*** end dem2rad.c ***/

