DELAYS.C

/*********************************************************************
        (c) 1984-2003 by Scientific Endeavors Corporation.
        All rights reserved.
    This program plots air traffic performance for
    airport departures using the data file BOSDEP.DAT.

    This program illustrates several useful GraphiC techniques:
      1) How to create your own nonlinear polar grid.
      2) How to generate a "clock" plot.
      3) How to use circtext() and pltfnt().
      4) How to make filled characters with a different color outline.
      5) How to access stdprn in protected mode (GraphiC-286).
*********************************************************************/

#include <graphic.h>
#define TWOPI   (float)TWO_PI
#define PI2     (float)PI_OVER_TWO

#if TCQ                            /* Set stack for Borland (Turbo) C */
extern unsigned _stklen = 0x3000;
#endif

FILE *ifptr = NULL, *stdp = NULL;
char lab[10];                                           /* The labels */

/**********************************************************************/
float rmap(float rin);
void GPC_MAIN(int argc, char *argv[])
{
    float rmin = 0.0f, rmax = 15.0f, r0 = 10.0f, rinner = 5.0f, dr;
    float r[241], theta[241], dth, x1, y1, xc, yc;
    float r2[2], th2[2], rw, temp, th;
    int i, j, nth = 240;
    int ntick = 24;
    char tkfname[20], name[6], str[100];
    char *cp;
    int type;
    float actftime, oagdz, dz, az, estftime, oagaz;
    char flight[10], from[10], to[10], depflight[12], equip[6];
    float airdelay, grnddelay, arrivedelay;
    int hour, tenth;
    char datname[20];

    if(argc < 2)
        strcpy(datname, "bosdep.dat");
    else
        strcpy(datname, argv[1]);

    ifptr = gfopen(datname, "r", 0);
               /* Looks in the GPC(286) directory for the file */
    if(ifptr==NULL) {
        GPC_PUTS("\nUnable to open input data file.");
        goto EndOfApp;
    }
/*
Using GraphiC-286 in protected mode, stdprn is not opened by default. If you
debug by sending messages to stdprn, here is how to open a file handle
that can be used for that purpose.
*/
#if (1-GPC_WIN)
    stdp = fopen("LPT1", "w");         /* The printer is not actually used */
#endif
/* TOSS THE ONE-LINE HEADER IN INPUT DATA */
/*
    HEADER FORMAT:
    DepFlight  DZType Flight  Equip  From  To  DZ  EstArr  OAGDep  OAGArr  AZ
*/
    while(fgetc(ifptr) != '\n');

/* CREATE A CONTEXT-DEPENDENT TKF FILE NAME AND TITLE */
    strcpy(tkfname, datname);
    cp = tkfname+3;
    strcpy(cp, "DELAY.TKF");
    strncpy(name, datname, 3);
    name[3] = '\0';
    cp = (char *)strupr(name);

    bgnplot(1, 'g', tkfname);                             /* Start GraphiC */
    font(4,"simplex.fnt",'\310',"swiss.fnt",'\311',"compgrma.fnt",'\312',
        "swissitl.fnt", '\313');                             /* Load fonts */
    fillfont(1);                                              /* Fill them */
    startplot(230);                               /* Background is white */
    metricunits(0);                        /* Ensure scaling in inch units */
    color(BLACK);                                      /* Scales are black */
    rotate(1);                                            /* Portrait mode */
    page((float)6.844, (float)9.0);
    area2d((float)6.0, (float)6.0);
    physor((float)0.4, (float)1.5);        /* Shift the plot down the page */

/* MAKE A LEGEND */
    legpos(3, (float)0.2, (float)1.0, 1);
    legend(2,"\311before takeoff = DZ time - OAG departure time", -2, .15f, 2);
    legend(2,"\311in air = actual flight time - estimated flight time", -1, .15f, 1);
    legend(2,"\311on arrival = AZ time - OAG arrival time", -4, .15f, 4);

/* PROCEDURE FOR OUTLINING A FONT IN ANOTHER COLOR */
    tmargin((float)0.0);
    color(RED);                             /* Do the filled string in red */
    sprintf(str, "\313Departure Performance at %s", cp);
    ctline(str, (float).3);     /* ctline() gives more control of position */
    tmargin((float)0.0);                           /* than using heading() */
    color(BLACK);                            /* The outline color is black */
    fillfont(0);                       /* Turn off filling for the outline */
    ctline(str, (float).3);
    fillfont(1);
    axesoff(AXESOFF);                      /* We want to draw our own axes */
                       /* The plot will be an annulus from */
                                     /* rinner to rmax */
    polgrid(rmax, rmin, 1, NULL, 0, 0, 0);     /* Set up polar coordinates */
    dth = TWOPI/(float)nth;

/* DRAW BASE CIRCLE */
    for (i = 0; i < nth + 1; i++) {
        r[i] = r0;                /* The location of our "zero delay" axis */
                       /* Time goes clockwise from the top */
        theta[i] = PI2 - (float)i * dth;
    }
    tcurve(8);                              /* Thicken the zero delay line */
    polcurve(r, theta, nth+1, 0);               /* Draw it with no symbols */
    tcurve(1);
    dashf(2);                                 /* Dotted lines for the grid */

/* DRAW CIRCLES */
    for (j = -5; j <= 5; j++) {
        if(j == 0)
            continue;
/* DO NONLINEAR R SCALE */
        rw = rmap((float)j) + 10.0f;
        for (i = 0; i < nth + 1; i++) {
            r[i] = rw;
        }
        polcurve(r, theta, nth+1, 0);
    }

/* DRAW THE RADIAL LINES */
    dth = TWOPI/(float)ntick;
    dr = (float).3333333 * (rmax - rmin);
    r2[0] = r0 - dr;
    r2[1] = r0 + dr;
    for (i = 0; i < ntick; i++) {
        temp = PI2 - (float)i * dth ;
        th2[0] = th2[1] = temp;
        polcurve(r2, th2, 2, 0);
    }
    dashf(1);                                     /* Return to solid lines */
    color(BLUE);

/* LABEL THE CLOCK HOURS */
    dth = TWOPI/(float)ntick;

/* FIND CENTER OF CIRCLE */
    usertoinch((float)0.0, (float)0.0, &xc, &yc);

/* FIND THE RADIUS (= x1 - xc) */
    usertoinch(rmax - (float).7, (float)0.0, &x1, &y1);
    for (i = 0; i < ntick; i ++) {
        th = PI2 - (float)i * dth;                          /* Clock angle */
        sprintf(lab, "\311%d:00", i);
        circtxt(xc, yc, x1 - xc, lab, (float).1,
            (th*(float)360./TWOPI) + (float)2.5);
    }

/* Add a curved axis label. Note the font change to get an arrow */
    circtxt(xc, yc, x1 - xc + (float).25, "\311Greenwich Mean Time \312:\311",
        (float).12, (float)90.0);

/* RADIAL LABELS */
    tline(6);
    color(GREEN);

/* MAKE TWO RADIAL ARROWS - DON'T CROSS THE ZERO DELAY LINE */
    temp = (float)(.875 * (double)TWOPI);                   /* 315 degrees */
    uvector(r0 - (float).1, temp, rinner - (float)1.0,
        temp, (float)2.5, (float).1, "01");
    pltfnt(rinner - (float)4.5, temp, "\311Early", (float).2, -45);
    color(RED);
    uvector(r0 + (float).1, temp, rmax + (float)0.5,
        (float)(.875*(double)TWOPI), (float)2.5, (float).1, "01");
    pltfnt(rmax + (float)1.5, temp, "\311Late", (float).2, -45);
    tline(2);
    color(BLUE);
    temp = (float)(.885 * (double)TWOPI);
    pltfnt(rinner + (float)2.0, temp, "\311hours", (float).1, -45);

/* THE PLOT IS SET UP. LOOP THROUGH THE DATA AND PLOT EACH POINT */
    for(;;) {
/* GET FIRST DATA */
        if(fscanf(ifptr, "%s%d%s%s%s%s%f%f%f%f%f",
            &depflight, &type, &flight, &equip, &from, & to,
            &dz, &estftime, &oagdz, &oagaz, &az) == EOF)
            break;
                  /* Get nearest 6-minute interval (= .1 hour) */
        i = (int)(dz * 100.f);
        hour = i/100;
        tenth = (i%100)/10;
        j = 10 * hour + tenth;
        temp = theta[j] * (float)360./TWOPI;  /* Need degrees for the call */

/* CALCULATE AIR DELAY */
/* The difference between the estimated flight time in the dz message and (az - dz) */
        if( az == (float)-1.0 || dz == (float)-1.0 || estftime == (float)-1.0)
            airdelay = (float)-100.0;               /* Check for NULL data */
        else {
            if(az < dz)
                actftime = (float)24.0 + az - dz;   /* Adjust for midnight */
            else
                actftime = az - dz;
            if(estftime < dz)
                estftime += (float)24.f;
            estftime = estftime - dz;
            airdelay = actftime - estftime;
        }

/* CALCULATE GROUND DELAY BEFORE TAKEOFF */
/* THE TIME BETWEEN ACTUAL TAKEOFF AND OAG START */
        if(oagdz == (float)-1.0 || dz == (float)-1.0)
            grnddelay = (float)-100.0;
        else {
            if(fabs(oagdz - dz) > (float)12.0) {/* Across 24-hour boundary */
                if(dz < oagdz)
                    dz += (float)24.0;
                else
                    oagdz += (float)24.0;
            }
            grnddelay = dz - oagdz;
        }

/* CALCULATE ARRIVAL DELAYS */
        if(oagaz == (float)-1.0 || az == (float)-1.0)
            arrivedelay = (float)-100.0;
        else {
            if(fabs(oagaz - az) > (float)12.0) {/* Across 24-hour boundary */
                if(az < oagaz)
                    az += (float)24.0;
                else
                    oagaz += (float)24.0;
            }
            arrivedelay = az - oagaz;
        }

/* GRAPH THE ITEMS */
        if(grnddelay != (float)-100.0) {
                    /* Get the correct position on the nonlinear scale */
            rw = rmap(grnddelay) + (float)10.;
            color(GREEN); /* Plot each data point as a small angular wedge */
            wedge(rw - (float).03, rw + (float).03, temp-(float).6,
                temp +(float).6, -2, 1);
        }
        if(airdelay != (float)-100.) {
            rw = rmap(airdelay) + (float)10.0;
            color(BLUE);
            wedge(rw - (float).03, rw + (float).03, temp-(float).6,
                temp +(float).6, -1, 1);
        }
        if(arrivedelay != (float)-100.0) {
            rw = rmap(arrivedelay) + (float)10.;
            color(RED);
            wedge(rw - (float).03, rw + (float).03, temp-(float).6,
                temp +(float).6, -4, 1);
        }
    }
    gfclose(ifptr);
    color(BLACK);
    dateit((Uchar)'\310');
    endplot();
    stopplot();

EndOfApp:
    if (stdp != NULL)
        fclose(stdp);
}

/***********************************************************************/
/*
    Make a nonlinear logarithmic radial scale to expand the first hours.
    rout =  5 * sgn(rin) * log10(|rin|+1)/log10(6)
*/
float rmap(float rin)
{
    float temp;

    if(rin < 0) {
        temp = (float)-5.0 * (float)log10((double)-(rin - (float)1.0))/(float)log10((double)6.0);
    }
    else {
        temp = (float)5.0 * (float)log10((double)(rin + (float)1.0))/(float)log10((double)6.0);
    }

    return(temp);
}