/*
 * FILE:  vector.c
 *
 */

#include "common.h"
#include "io_.h"
#include "io_nc.h"  /* get_quotation */
#include "mem_.h"   /* move_byte */
#include <time.h>
#include "map.h"
#include "ioserv.h"
#include "vecplot.h"

/* 1999/07/13 Pierre Jaccard (A4) */
#define INCH_PER_MM 0.03937

#define VSPACE 0.33 /* vertical whitespace allowance set to 1/3 of font size */
#define font_width(font_size) (font_size)/1.5 /* width is roughly 2/3 of height */
#define is_multiple(x,y) (fabs(frem((x),(y))) == 0)

static NAME_LIST_ENTRY_TYPE SymbolList[] =
{
   {"circle"            , CIRCLE},
   {"filled_circle"     , CIRCLE | FILLED},
   {"delta"             , DELTA},
   {"filled_delta"      , DELTA | FILLED},
   {"del"               , DEL},
   {"filled_del"        , DEL | FILLED},
   {"square"            , SQUARE},
   {"filled_square"     , SQUARE | FILLED},
   {"diamond"           , DIAMOND},
   {"filled_diamond"    , DIAMOND | FILLED},
   {"plus"              , PLUS},
   {"x"                 , XMARK},
   {"none"        , NONE},
   {NULL                , BADINT}
};

FILE *FpPs = NULL;
static LABEL_TYPE *CommonLabelsHead, *LocalLabelsHead;

TITLE PlotTitle, MainTitle, SubTitle;
static char TimeStamp[80], /* for plot time stamp, user-settable */
            TodayDT[80],   /* for PostScript Creation Date, from system clock */
            PsFile[80], VecFiles[MAXNUMFILES][80],
            Annotation[80], Orientation[10];

/* 1999/07/13 Pierre Jaccard (A4) */
static char PaperSize[10];

static FONT TitleFont;
static int IndexUV, PlotCruiseTrack,
           ShowPage, NewPage, NegativeLongitudes;
static MARGIN PageMargin, PlotMargin;
static XY_TYPE PageDim_i,
               PageSpace_i,  /* page dimensions less page margins */
               PlotDim_i,
               PlotSpace_i;   /* maximum plot/subplot size from axis to axis */
int Bb[4];                      /* bounding box coordinates */
XY_TYPE DegPerInch,                     /* plot scale in degrees per inch */
        Origin_i;                       /* plot origin in inches */
static RANGE_INTERVAL LonRange, LatRange;
static PLOT_ARRAY PlotArray;    /* order of plots on the page (M x N) */
static LEGEND_TYPE Legend;
static AXIS_TYPE Axis;
static ARROW_TYPE Arrow;
static struct
{
   FILE_NAME_TYPE file;
   double         gray_level, linewidth_p;
} Map;

int PageCount;

static OPTION_TYPE LegendOptions[] =
{
   "end"        , 0             , NULL          , NULL,
   "label:"     , 0             , read_quotation, Legend.text,
   "font:"      , TYPE_STRING   , op_get_param  , Legend.font.face,
   "font_size:" , TYPE_DOUBLE   , op_get_param  , (char *) &Legend.font.size_p,
   "position:"  , COORDINATE    , read_xy       , (char *) &Legend.pos,
   "length:"    , TYPE_DOUBLE   , op_get_param  , (char *) &Legend.length,
   "type:"      , TYPE_STRING   , op_get_param  , Legend.type,
   "linewidth:" , TYPE_DOUBLE   , op_get_param  , (char *) &Legend.linewidth_p,
   NULL         , 0             , NULL          , NULL
};

static OPTION_TYPE ArrowOptions[] =
{
   "end"                , 0             , NULL          , NULL,
   "headlength:"        , TYPE_DOUBLE   , op_get_param  , (char *) &Arrow.headlength_i,
   "min_headlength:"    , TYPE_DOUBLE   , op_get_param  , (char *) &Arrow.min_headlength_i,
   "linewidth:"         , TYPE_DOUBLE   , op_get_param  , (char *) &Arrow.linewidth_p,
   "scale:"             , TYPE_DOUBLE   , op_get_param  , (char *) &Arrow.cms_per_inch,
   NULL                 , 0             , NULL          , NULL
};

static OPTION_TYPE AxisOptions[] =
{
   "end"                , 0             , NULL          , NULL,
   "font:"              , TYPE_STRING   , op_get_param  , Axis.font.face,
   "font_size:"         , TYPE_DOUBLE   , op_get_param  , (char *) &Axis.font.size_p,
   "linewidth:"         , TYPE_DOUBLE   , op_get_param  , (char *) &Axis.linewidth_p,
   "decimal_place:"     , COORDINATE    , read_xy       , (char *) &Axis.decimal_place,
   "label_interval:"    , COORDINATE    , read_xy       , (char *) &Axis.label_interval,
   "origin:"            , COORDINATE    , read_xy       , (char *) &Axis.origin_i,
   "scale:"             , SCALE         , read_xy       , (char *) &Axis.deg_per_inch,
   NULL                 , 0             , NULL          , NULL
};

static OPTION_TYPE MapOptions[] =
{
   "end"                , 0             , NULL          , NULL,
   "file:"              , TYPE_STRING   , op_get_param  , Map.file,
   "shading:"           , TYPE_DOUBLE   , op_get_param  , (char *) &Map.gray_level,
   "linewidth:"         , TYPE_DOUBLE   , op_get_param  , (char *) &Map.linewidth_p,
   NULL                 , 0             , NULL          , NULL
};

static OPTION_TYPE Options[] =
{
   "end"                , 0             , NULL          , NULL,

   /*--- PLOT SCALE & PLACEMENT ---*/
   "new_page"           , 0             , showpage      , NULL,
   "subplot:"           , 0             , subplot       , NULL,
   "orientation:"       , TYPE_STRING   , op_get_param  , (char *) Orientation,
   "top_margin:"        , TYPE_DOUBLE   , op_get_param  , (char *) &PageMargin.top,
   "bottom_margin:"     , TYPE_DOUBLE   , op_get_param  , (char *) &PageMargin.bottom,
   "left_margin:"       , TYPE_DOUBLE   , op_get_param  , (char *) &PageMargin.left,
   "right_margin:"      , TYPE_DOUBLE   , op_get_param  , (char *) &PageMargin.right,

   /*--- VECTOR PLOTTING OPTIONS ---*/
   "vecfile:"           , 1             , read_files    , (char *) VecFiles,
   "vecfiles:"          , 0             , read_files    , (char *) VecFiles,
   "u_v_pair:"          , TYPE_INT      , op_get_param  , (char *) &IndexUV,
   "cruise_track"       , 1             , set_code      , (char *) &PlotCruiseTrack,
   "arrow:"             , ECHO          , execute_options, (char *) ArrowOptions,

   /*--- MAP PLOTTING OPTIONS ---*/
   "map:"               , ECHO          , execute_options, (char *) MapOptions,

   /*--- AXES CONTROL ---*/
   "lon_range:"         , LONGITUDE     , read_range    , (char *) &LonRange,
   "lat_range:"         , LATITUDE      , read_range    , (char *) &LatRange,
   "axis:"              , ECHO          , execute_options, (char *) AxisOptions,

   /*--- PLOT TITLES/ANNOTATIONS ---*/
   "main_title:"        , MAIN_TITLE    , read_quotation, MainTitle.text,
   "sub_title:"         , SUBTITLE      , read_quotation, SubTitle.text,
   "plot_title:"        , PLOT_TITLE    , read_quotation, PlotTitle.text,

   "title_font:"        , TYPE_STRING   , op_get_param  , TitleFont.face,
   "title_font_size:"   , TYPE_DOUBLE   , op_get_param  , (char *) &TitleFont.size_p,

   "label:"             , 0             , read_label    , NULL,
   "clear_labels"       , 0             , clear_labels  , (char *) &CommonLabelsHead,

   "legend:"            , ECHO          , execute_options, (char *) LegendOptions,
   "time_stamp:"        , 0             , read_quotation, TimeStamp,
   "annotation:"        , 0             , read_quotation, Annotation,

   /*--- OUTPUT OPTIONS ---*/
   "psfile:"            , TYPE_STRING   , op_get_param  , PsFile,
   "make_plot"          , 0             , make_plot     , NULL,
   "add_plot"           , 1             , make_plot     , NULL,

   /*--- MISC. OPTIONS ---*/
   "skip_to:"           , 0             , skip_to       , NULL,
   NULL                 , 0             , NULL          , NULL
};

#if PROTOTYPE_ALLOWED
void do_it(FILE * fpcon)
#else
void do_it(fpcon)
   FILE *fpcon;
#endif
{

   set_defaults();
   while(execute_options(fpcon, Options, ECHO) > 0)
      ;
   if (FpPs != NULL)
   {
      showpage(FpPs, NULL, 0);   /* Always closes FpPs. */
   }
   fputs("\n Done.\n", stderr);
}

#if PROTOTYPE_ALLOWED
int make_plot(FILE * fpcon, char *dummy, int append_flag)
#else
int make_plot(fpcon, dummy, append_flag)
   FILE *fpcon;
   char *dummy;
   int append_flag;
#endif
{
   double cos_lat, fontspace;
   int rc;

   fflush(stdout);  /* display all options for this plot */
   if (PlotArray.current <= 0)
      return(-1);
   NegativeLongitudes = 0;
   if (LonRange.min < 0.0 || LonRange.max < 0.0)
      NegativeLongitudes = 1;
   if (LonRange.min > 180.0 || LonRange.max > 180.0)
      NegativeLongitudes = 0;

   /*----------------- determine plot size & placement -------------------*/
   if (!stricmp(Orientation, "landscape"))
   {
      PageDim_i.x = 11.0;
      PageDim_i.y = 8.5;
      /* 1999/07/13 Pierre Jaccard (A4) */
      if(!stricmp(PaperSize, "a4")){ 
        PageDim_i.x = 297.0 * INCH_PER_MM;
        PageDim_i.y = 210.0 * INCH_PER_MM;
      }
   }
   else
   {
      PageDim_i.x = 8.5;
      PageDim_i.y = 11.0;
      /* 1999/07/13 Pierre Jaccard (A4) */
      if(!stricmp(PaperSize, "a4")){ 
        PageDim_i.x = 210.0 * INCH_PER_MM;
        PageDim_i.y = 297.0 * INCH_PER_MM;
      }
   }
   PageSpace_i.x = PageDim_i.x - PageMargin.left - PageMargin.right;
   PageSpace_i.y = PageDim_i.y - PageMargin.top - PageMargin.bottom;

   fontspace = 1.0 + VSPACE;  /* adding some vertical white space to font */
   if (strcmp(MainTitle.text, ""))
      PageSpace_i.y -= MainTitle.font.size_i * fontspace;
   if (strcmp(SubTitle.text, ""))
      PageSpace_i.y -= SubTitle.font.size_i * fontspace;

   Axis.font.size_i = Axis.font.size_p / DPI;
   if (NewPage)
   {
      PlotMargin.top = 0.25 + PlotTitle.font.size_i * fontspace;
      PlotMargin.bottom = Axis.font.size_i * fontspace;
      PlotMargin.left = (4 + (Axis.decimal_place.y >= 0 ? Axis.decimal_place.y :
                        get_decimal_length(LatRange.step * Axis.label_interval.y)))
                        * font_width(Axis.font.size_i);
      /* we won't bother to set PlotMargin.right based on a similar logic
         because the rightmost tick may or may not get a numeric label depending
         on LonRange.max and Axis.label_interval.x settings; besides, we do check
         if there is enough space for the label later in plot_axes.  Instead,
         we'll leave it fixed at 0.25in. */
      PlotMargin.right = 0.25;
   }
   PlotSpace_i.x = PageSpace_i.x / PlotArray.n - PlotMargin.left - PlotMargin.right;
   PlotSpace_i.y = PageSpace_i.y / PlotArray.m - PlotMargin.top - PlotMargin.bottom;

   if (LonRange.span == 0.0 || LatRange.span == 0.0)
   {
      fputs("\n Nothing to plot!\n", stderr);
      return (-1);
   }

   if (Axis.deg_per_inch.x < 0.0 || Axis.deg_per_inch.y < 0.0)  /* autoscale = max. mercator */
   {
      cos_lat = cos(M_PI * 0.5 * (LatRange.max + LatRange.min) / 180.0);
      /* 1) LonRange.span / DegPerInch.x <= PlotSpace_i.x;
         2) LatRange.span / DegPerInch.y <= PlotSpace_i.y;
         3) DegPerInch.y = DegPerInch.x * cos_lat;
         Solving above gives: */
      DegPerInch.x = larger(LonRange.span / PlotSpace_i.x,
                            LatRange.span / (PlotSpace_i.y * cos_lat));
      DegPerInch.y = DegPerInch.x * cos_lat;
   }
   else
   {
      DegPerInch.x = Axis.deg_per_inch.x;
      DegPerInch.y = Axis.deg_per_inch.y;
   }
   PlotDim_i.x = LonRange.span / DegPerInch.x;  /* in inches */
   PlotDim_i.y = LatRange.span / DegPerInch.y;

   fprintf(stderr,
      "\n\n => plot size: %.2f (longitude) by %.2f (latitude) inches\n",
      PlotDim_i.x, PlotDim_i.y);

/*
   if (PlotSpace_i.x - PlotDim_i.x < -0.000001 ||
       PlotSpace_i.y - PlotDim_i.y < -0.000001)
   {
      fputs(" ERROR:  Unable to fit plot on page.\n", stderr);
      fprintf(stderr, " Maximum plot size is %f\" x %f\" \n",
         PlotSpace_i.x, PlotSpace_i.y);
      fputs(" You can:  1) change the scale\n", stderr);
      fputs("           2) reduce the range\n", stderr);
      if (stricmp(Orientation, "portrait"))
         fputs("           3) specify portrait Orientation\n", stderr);
      return (-1);
   }
*/
   if (Axis.origin_i.x == -1.0 && Axis.origin_i.y == -1.0)  /* autocenter */
   {
      Origin_i.x = PageMargin.left +
                   (PlotArray.current - 1) % PlotArray.n *
                      (PlotSpace_i.x + PlotMargin.left + PlotMargin.right) +
                   PlotMargin.left +
                   (PlotSpace_i.x - PlotDim_i.x) / 2.0;  /* center within plot space */
      Origin_i.y = PageMargin.bottom +
                   (PlotArray.m - 1 - (PlotArray.current - 1) / PlotArray.n) *
                      (PlotSpace_i.y + PlotMargin.top + PlotMargin.bottom) +
                   PlotMargin.bottom +
                   (PlotSpace_i.y - PlotDim_i.y) / 2.0;
   }
   else
   {
      Origin_i.x = Axis.origin_i.x;
      Origin_i.y = Axis.origin_i.y;
   }

   /*------------------- initialize postscript file ----------------------*/
   if (PsFile[0])
   {
      if (!append_flag || !EXISTS(PsFile))
      {
         FpPs = fopen(PsFile, "w");
         PageCount = append_flag = 0; /* just in case it's set but file d.n.e. */
      }
      else
      {
         if (NewPage)
         {
            FpPs = fopen(PsFile, "r+");
            /*
             * position to read num. of pages so far then to overwrite
             * '%%Trailer, ...'
             */
            if (fseek(FpPs, -17L, 2) != 0 ||
               fscanf(FpPs, "Pages: %d", &PageCount) != 1 ||
               fseek(FpPs, -60L, 2) != 0)
            { /* above are offsets for text files where lines are terminated by 1 char only (LF);
                 below are offsets for DOS text files with lines terminated by 2 chars (CRLF) */
               if (fseek(FpPs, -19L, 2) != 0 ||
                  fscanf(FpPs, "Pages: %d", &PageCount) != 1 ||
                  fseek(FpPs, -64L, 2) != 0)
               {
                  fprintf(stderr, "ERROR: Unable to read trailer in PostScript file %s.\n",
                     PsFile);
                  return (-1);
               }
            }
         }
      }
      if (FpPs == NULL)
      {
         fprintf(stderr, " ERROR: Unable to open postscript file %s.\n", PsFile);
         return (-1);
      }
   }
   else
      FpPs = stdout;
   fflush(stderr);
   if (!append_flag)
      initps(FpPs, MainTitle.text, TodayDT);    /* write PostScript prologue */
   if (NewPage)
   {
      ++PageCount;
      fprintf(FpPs, "%%%%Page: %d\n\n", PageCount);
      Bb[llx] = Bb[lly] = MAXINT;
      Bb[urx] = Bb[ury] = 0;
      NewPage = 0;
   }
   /*-------------- update bounding box -----------------*/
   Bb[llx] = smaller(Bb[llx], DPI * (Origin_i.x - PlotMargin.left));
   Bb[lly] = smaller(Bb[lly], DPI * (Origin_i.y - PlotMargin.bottom));
   Bb[urx] = larger(Bb[urx], DPI * (Origin_i.x + PlotDim_i.x + PlotMargin.right));
   Bb[ury] = larger(Bb[ury], DPI * (Origin_i.y + PlotDim_i.y + PlotMargin.top));

   fputs("%%BeginObject:\n", FpPs);
   /* Save the original state; restore it at the end of the object. */
   fputs("gsave\n", FpPs);
   /* landscape = CW rotation, then drop origin down 8.5 inches (612 pts) */
   if (!stricmp(Orientation, "landscape"))
      fputs("90 rotate  0 -612 translate\n", FpPs);

   /* plotting scale is reset to measure in inches */

   fprintf(FpPs, "%d %d scale\n", DPI, DPI);
   fprintf(FpPs, "%f %f translate\n", Origin_i.x, Origin_i.y);

   if (Map.file[0])
   {
      fprintf(FpPs, "%f setlinewidth\n", Map.linewidth_p / DPI);
      drawmap(Map.file, FpPs, *((RANGE_TYPE *) &LonRange), *((RANGE_TYPE *) &LatRange), Map.gray_level);
   }
   if (strcmp(PlotTitle.text, ""))
   {
      fprintf(FpPs, "%f /%s Fsi\n", PlotTitle.font.size_i, PlotTitle.font.face);
      fprintf(FpPs, " (%s) %f %f SC\n\n", PlotTitle.text,
         PlotDim_i.x / 2.0,
         PlotDim_i.y + VSPACE * PlotTitle.font.size_i);
   }
   plot_axes();
   if (IndexUV > 0 && Legend.text[0] && !strcmp(Legend.type, "local"))
      plot_legend();
   rc = plot_vectors();
   if (rc)
      return (-1);
   plot_labels(CommonLabelsHead);
   plot_labels(LocalLabelsHead);
   clear_labels(FpPs, &LocalLabelsHead, 0);

   fputs("grestore\n", FpPs);/* complete reset to original */
   if (ShowPage)
      showpage(FpPs, NULL, 0);

   fputs("\n Plot finished.\n", stderr);
   return (0);
}

#if PROTOTYPE_ALLOWED
int showpage(FILE * fp, char *arg, int code)
#else
int showpage(fp, arg, code)
   FILE *fp;
   char *arg;
   int code;
#endif
{
   double title_y;

   if (FpPs == NULL)
      return(-1);
   fputs("gsave\n", FpPs);
   /* landscape = CW rotation, then drop origin down 8.5 inches (612 pts) */
   if (!stricmp(Orientation, "landscape"))
      fputs("90 rotate  0 -612 translate\n", FpPs);

   /* plotting scale is reset to measure in inches */
   fprintf(FpPs, "%d %d scale\n", DPI, DPI);

   /* time stamp is set in 0.25in (right-aligned), 0.35in and
    * annotation is set in 0,25in (right-aligned), 0.50in
    * from the upper right corner of the page */

   if (strcmp(TimeStamp, ""))
   {
      fprintf(FpPs, "%f /Times-Italic Fsi\n", 10.0 / DPI);
      fprintf(FpPs, "(%s) %f %f SR\n", TimeStamp,
         (PageDim_i.x - 0.25), (PageDim_i.y - 0.35));
   }
   if (strcmp(Annotation, ""))
   {
      if (!strcmp(TimeStamp, ""))
         fprintf(FpPs, "%f /Times-Italic Fsi\n", 10.0 / DPI);
      fprintf(FpPs, "(%s) %f %f SR\n", Annotation,
         (PageDim_i.x - 0.25), (PageDim_i.y - 0.50));
   }

   title_y = 1.0 * Bb[ury] / DPI;
   /* Bb[ury] is highest plot's top axis + PlotMargin.top, in points */
   if (SubTitle.text[0])
   {
      print_heading(&SubTitle, title_y);
      title_y += SubTitle.font.size_i * (1 + VSPACE);
   }

   if (MainTitle.text[0])
      print_heading(&MainTitle, title_y);

   fprintf(FpPs, "%f setlinewidth\n", Axis.linewidth_p / DPI);
   if (IndexUV > 0 && Legend.text[0] && !stricmp(Legend.type, "common"))
      plot_legend();

   fputs("grestore\n", FpPs);
   fprintf(FpPs, "showpage\n%%%%PageBoundingBox: %d %d %d %d\n",
           Bb[llx], Bb[lly], Bb[urx], Bb[ury]);
   fprintf(FpPs, "%%%%Trailer\n%%%%BoundingBox: %3d %3d %3d %3d\n%%%%Pages: %3d\n%%%%EOF\n",
           Bb[llx], Bb[lly], Bb[urx], Bb[ury], PageCount);
   fclose(FpPs);
   FpPs = NULL;
   NewPage = 1;
   return 0;
}

#if PROTOTYPE_ALLOWED
int plot_vectors(void)
#else
int plot_vectors()
#endif
{
   FILE *fpvec;
   LONLAT_TYPE ll_i[MAXLONLAT], /* inches */
               ll_d;            /* degrees */
   double angle, speed, u, v;   /* m/s */
   char buff[255];
   int i, npt;
   int ifile;

   if (IndexUV > 0)
   {
      if (Arrow.min_headlength_i < 0.0)
         Arrow.min_headlength_i = 0.5 * Arrow.headlength_i;
      fputs("%---Vectors---\n", FpPs);
      fprintf(FpPs, "%f setlinewidth\n", Arrow.linewidth_p / DPI);
      fprintf(FpPs, "/headlength %f def\n", Arrow.headlength_i);
      fprintf(FpPs, "/halfheadthickness %f def\n", 0.25 * Arrow.headlength_i * 1.67 * Arrow.linewidth_p);
      fprintf(FpPs, "/arrowhead_min  %f def\n", Arrow.min_headlength_i);
   }
   for (ifile = 0; stricmp(VecFiles[ifile], "end"); ifile++)
   {
      if ((fpvec = fopen(VecFiles[ifile], "r")) == NULL)
      {
         fprintf(stderr, "ERROR: Unable to open vector file %s\n.",
                 VecFiles[ifile]);
         return (-1);
      }
      npt = 0;
      while (fscanf(fpvec, " %lf %lf", &(ll_d.lon), &(ll_d.lat)) == 2)
      {
         /*
          * the first pair of columns is position (lon-lat in degrees),
          * followed by column pairs of zonal & meridional velocities in cm/s
          */
         if (ll_d.lon < LonRange.min)
            ll_d.lon += 360.0;
         if (ll_d.lon > LonRange.max)
            ll_d.lon -= 360.0;
         if (ll_d.lon >= LonRange.min && ll_d.lon <= LonRange.max &&
             ll_d.lat >= LatRange.min && ll_d.lat <= LatRange.max)
         {
            ll_i[npt].lon = LonToIn(ll_d.lon);
            ll_i[npt].lat = LatToIn(ll_d.lat);
            if (IndexUV > 0)
            {
               for (i = 0; i < IndexUV - 1; i++)
                  fscanf(fpvec, "%*f %*f");   /* skip unwanted pairs */
               check_error((fscanf(fpvec, " %lf %lf", &u, &v) != 2),
                           "scanning U/V from vector file");
               if (u < 10000.0 && v < 10000.0)  /* good value to plot */
               {
                  speed = sqrt(u * u + v * v) * 100;    /* to cm/s */
                  /*
                   * All this special case checking is essential to avoid
                   * range errors or NaNs in the output.
                   */
                  angle = speed >= 1e-10 ?
                             (fabs(u) >= 1e-10 ? atan2(v, u) * 180 / M_PI :  /* normal case */
                                                 (fabs(v) / v) * 180 / M_PI) :
                             0.0;

                  fprintf(FpPs, "%9.4f %7.2f %9.4f %9.4f A\n",
                          speed / Arrow.cms_per_inch, angle,
                          ll_i[npt].lon, ll_i[npt].lat);
               }
            }
            if (PlotCruiseTrack)        /* need to save points */
            {
               npt++;
               check_error((npt >= MAXLONLAT), "max. array size exceeded");
            }
         }
         fgets(buff, 255, fpvec);       /* Discard remainder of line. */
      }
      fclose(fpvec);

      if (npt > 1)
      {
         fputs("%---Cruise Track---\n", FpPs);
         fprintf(FpPs, "%f setlinewidth\n", 0.5 * Arrow.linewidth_p / DPI);

         fprintf(FpPs, "%9.4f %9.4f M\n", ll_i[0].lon, ll_i[0].lat);
         for (i = 1; i < npt; i++)
         {
            fprintf(FpPs, "%9.4f %9.4f L\n", ll_i[i].lon, ll_i[i].lat);
         }
         fputs("stroke\n", FpPs);
      }
   }                            /* End of loop through vector files. */
   return 0;
}

#if PROTOTYPE_ALLOWED
void print_heading(TITLE * title, double y)
#else
void print_heading(title, y)
   TITLE *title;
   double y;
#endif
{
   fprintf(FpPs, "%f /%s Fsi\n", title->font.size_i, title->font.face);
   fprintf(FpPs, "(%s) %f %f SC\n", title->text,
           0.5 * (Bb[llx] + Bb[urx]) / DPI, y);
   /* add vertical title space to bounding box */
   Bb[ury] = larger(Bb[ury], DPI * (y + title->font.size_i));
}

#if PROTOTYPE_ALLOWED
int read_label(FILE * fp, LABEL_TYPE *arg, int code)
#else
int read_label(fp, arg, code)
   FILE *fp;
   LABEL_TYPE *arg;
   int code;
#endif
{
   static LABEL_TYPE label;
   static char label_type[10], label_symbol[20], label_just[10];
   static NAME_LIST_ENTRY_TYPE just[] =
   {
      "left"    , J_LEFT,
      "right"   , J_RIGHT,
      "center"  , J_CENTER,
      NULL      , BADINT
   };
   static OPTION_TYPE label_options[] =
   {
      "end"             , 0             , NULL          , NULL,
      "type:"           , TYPE_STRING   , op_get_param  , label_type,
      "text:"           , 0             , read_quotation, label.text,
      "symbol:"         , TYPE_STRING , op_get_param, label_symbol,
      "position:"       , COORDINATE    , read_xy       , (char *) &label.position,
      "justify:"        , TYPE_STRING   , op_get_param  , label_just,
      "font:"           , TYPE_STRING   , op_get_param  , label.font.face,
      "font_size:"      , TYPE_DOUBLE   , op_get_param  , (char *) &label.font.size_p,
      "symbol_size:"    , TYPE_DOUBLE   , op_get_param  , (char *) &label.symbol_size,
      NULL              , 0             , NULL          , NULL
   };

   static LABEL_TYPE *newlabel = NULL;

   /* set label defaults */
   if (newlabel != NULL) /* carry over the previous label's settings */
   {
      move_byte((char*) newlabel, (char *) &label, sizeof(LABEL_TYPE));
   }
   else  /* use program defaults */
   {
      strcpy(label_type, "common");
      strcpy(label_just, "left");
      strcpy(label.font.face, "Helvetica");
      label.font.size_p = 12;
      label.symbol_size = 4;
   }
   /* the following should always differ from label to label, hence reset: */
   label.text[0] = '\0';
   strcpy(label_symbol, "none");
   label.position.x = label.position.y = -BADVALUE;

   if ((newlabel = (LABEL_TYPE *) malloc(sizeof(LABEL_TYPE))) == NULL)
   {
      fprintf(stderr, "Error : Out of memory in update_label\n");
      return (1);
   }

   if (!execute_options(fp, label_options, ECHO))
      return(1);

   label.symbol = get_code(SymbolList, label_symbol);
   label.just   = get_code(just,       label_just);
   move_byte((char *) &label, (char *) newlabel, sizeof(LABEL_TYPE));

   if (!stricmp(label_type, "common"))
   {
      newlabel->next = CommonLabelsHead;        /* NULL or previous label */
      CommonLabelsHead = newlabel;
   }
   else
   {
      newlabel->next = LocalLabelsHead;
      LocalLabelsHead = newlabel;
   }

   return (0);
}

#if PROTOTYPE_ALLOWED
int read_quotation(FILE * fp, char *arg, int code)
#else
int read_quotation(fp, arg, code)
   FILE *fp;
   char *arg;
   int code;
#endif
{
   get_quotation(fp, arg, 80);

   switch (code)
   {
      case MAIN_TITLE:
         strcpy(MainTitle.font.face, TitleFont.face);
         MainTitle.font.size_i = (TitleFont.size_p <= 0.0 ?
                                 MainTitle.font.size_p : TitleFont.size_p) / DPI;
         break;
      case SUBTITLE:
         strcpy(SubTitle.font.face, TitleFont.face);
         SubTitle.font.size_i = (TitleFont.size_p <= 0.0 ?
                                SubTitle.font.size_p : TitleFont.size_p) / DPI;
         break;
      case PLOT_TITLE:
         strcpy(PlotTitle.font.face, TitleFont.face);
         PlotTitle.font.size_i = (TitleFont.size_p <= 0.0 ?
                                 PlotTitle.font.size_p : TitleFont.size_p) / DPI;
         break;
      default:
         break;
   }
   report_msg(arg);
   return (0);
}

#if PROTOTYPE_ALLOWED
int subplot(FILE * fp, char *arg, int code)
#else
int subplot(fp, arg, code)
   FILE *fp;
   char *arg;
   int code;
#endif
{
   int sp;

   ShowPage = code;

   if (fscanf(fp, "%d", &sp) != 1)
      return 1;
   PlotArray.m = (int)(sp / 100);
   PlotArray.n = (int)( (sp - PlotArray.m * 100) / 10 );
   PlotArray.current = sp % 10;
   if (PlotArray.current > PlotArray.m * PlotArray.n)
   {
      fprintf(stderr, "\n Invalid subplot value %d\n\n", sp);
      PlotArray.current = -1;
   }
   return 0;
}

#if PROTOTYPE_ALLOWED
void plot_legend()
#else
void plot_legend()
#endif
{
   int num_ticks, tick_no, long_tick;
   int precision, ii;
   double tick_length = 0.05,
          legend_width, legend_tick_inc, legend_length,  y_label;
   XY_TYPE legend_pos;

   /* we want the local vars (legend_pos and legend_length) to always
      be recalculated while their global counterparts (Legend.pos & Legend.length)
      always retain the original (user-specified or default) values
      so the legend length & position will automatically readjust
      if the velocity scale
      or other plot parameters are altered in subsequent plots */

   fputs("%---Legend---\n", FpPs);
   fprintf(FpPs, "%f setlinewidth\n", Legend.linewidth_p / DPI);
   legend_length = (Legend.length == -BADVALUE) ? Arrow.cms_per_inch : Legend.length;
   legend_width = legend_length / Arrow.cms_per_inch;
   Legend.font.size_i = Legend.font.size_p / DPI;
   if (Legend.pos.x == -BADVALUE || Legend.pos.y == -BADVALUE)
   {
      if (!strcmp(Legend.type, "local"))
      {  /* local legend's upper right corner is set 0.5",0.75"
            from lower right corner of plot */
         legend_pos.x = PlotDim_i.x - legend_width - 0.5;
         legend_pos.y = 0.75;
      }
      else
      {  /* common legend's upper right corner is set 0.5" from
            rightmost plot axis+margin and 0.25" from bottommost
            plot axis+margin */
         legend_pos.x = 1.0 * Bb[urx] / DPI - legend_width - 0.5;
         legend_pos.y = 1.0 * Bb[lly] / DPI - 0.25;
      }
   }
   else
   {
      legend_pos.x = Legend.pos.x;
      legend_pos.y = Legend.pos.y;
   }

   num_ticks = partition(legend_length, legend_width);
   legend_tick_inc = (legend_length / num_ticks) / Arrow.cms_per_inch;
   if (too_dense(legend_tick_inc))
   {
      /* if it's even-valued, then reduce to half;
         if multiple of 5, then reduce to a fifth;
         else forget the sub-ticks altogether... */
      long_tick = (num_ticks % 2 ?
                   (num_ticks % 5 ? num_ticks : 5) : 2);
      legend_tick_inc *= long_tick;
      num_ticks /= long_tick;
   }
   /* put long tick at intervals of 5 or 2 or none, as appropriate */
   long_tick = (num_ticks % 5 ?
                   (num_ticks % 2 ? num_ticks : 2) : 5);

   fprintf(FpPs, "%f /%s Fsi\n", Legend.font.size_i, Legend.font.face);

   /* Horizontal line, left to right. */
   fprintf(FpPs, " %f %f M %f 0.0 rlineto\n",
           legend_pos.x, legend_pos.y, legend_width);

   /* Left then right ticks and labels. */
   precision = get_decimal_length(legend_length);
   y_label = legend_pos.y - 2 * tick_length - (1.0 + VSPACE) * Legend.font.size_i;

   for (ii = 0; ii < 2; ii++)
   {
      fprintf(FpPs, " %f %f M 0.0 %f R\n",
              legend_pos.x + ii * legend_width,
              legend_pos.y,
              -2.0 * tick_length);
      fprintf(FpPs, " (%.*f) %f %f SC\n",
              precision, ii * legend_length,
              legend_pos.x + ii * legend_width,
              y_label);
   }

   /* Intermediate ticks; every long_tick one is 1.5 times longer. */
   for (tick_no = 1; tick_no < num_ticks; tick_no++)
   {
      fprintf(FpPs, " %f %f M 0.0 %f rlineto\n",
              legend_pos.x + legend_width * tick_no / num_ticks,
              legend_pos.y, -tick_length * (tick_no % long_tick ? 1.0 : 1.5));
   }
   fputs("stroke\n\n", FpPs);

   /* Title. */
   y_label -= (1 + VSPACE) * Legend.font.size_i;
   fprintf(FpPs, " (%s) %f %f SC\n", Legend.text,
           legend_pos.x + legend_width / 2.0,
           y_label);

   /* constants below approximate legend's space requirements in inches */
   Bb[llx] = smaller(Bb[llx], (legend_pos.x + 0.25) * DPI);
   Bb[lly] = smaller(Bb[lly], y_label * DPI);
   Bb[urx] = larger(Bb[urx], (legend_pos.x + legend_width + 0.25) * DPI);
   Bb[ury] = larger(Bb[ury], legend_pos.y * DPI);
}

#if PROTOTYPE_ALLOWED
void plot_axes()
#else
void plot_axes()
#endif
{
   double x_tick, y_tick;               /* tick positions in degrees */
   double big_tick, small_tick; /* Tic lengths in inches. */
   double x_tick_margin, y_tick_margin, x_tick_num;
   int lon_decimal, lat_decimal;
   char nsew;

   big_tick = 8 / 72.0;         /* 8 pts */
   small_tick = 0.5 * big_tick;
   x_tick_margin = -(1 + VSPACE) * Axis.font.size_i;
   y_tick_margin = -VSPACE * Axis.font.size_i;
   if (Axis.decimal_place.x < 0)
   {
      lon_decimal = get_decimal_length(LonRange.step * Axis.label_interval.x);
      lat_decimal = get_decimal_length(LatRange.step * Axis.label_interval.y);
   }
   else
   {
      lon_decimal = (int) Axis.decimal_place.x;
      lat_decimal = (int) Axis.decimal_place.y;
   }

   fputs("%---Axes---\n", FpPs);
   fprintf(FpPs, "%f setlinewidth\n", Axis.linewidth_p / DPI);
   fprintf(FpPs, "newpath\n");
   fprintf(FpPs, "%f %f M\n", 0.0, 0.0);
   fprintf(FpPs, "%f %f L\n", LonRange.span / DegPerInch.x, 0.0);
   fprintf(FpPs, "%f %f L\n", LonRange.span / DegPerInch.x, LatRange.span / DegPerInch.y);
   fprintf(FpPs, "%f %f L\n", 0.0, LatRange.span / DegPerInch.y);
   fprintf(FpPs, "closepath\n");

   fprintf(FpPs, "%f /%s Fsi\n", Axis.font.size_i, Axis.font.face);

   if (is_multiple(LonRange.max, LonRange.step) &&
       is_multiple(LonRange.max, LonRange.step * Axis.label_interval.x))
   {
      double hangover = 0.5 * (3 + (fabs(LonRange.max) > 0 ? (int)(log10(fabs(LonRange.max)))
                                                           : 0) + lon_decimal);
      /* hangover calculated as half the number of chars on a longtiude label =
         at least one numeral, plus the degree symbol and hemisphere (hence 3)
         and then more numerals if >= +/-10, and then the no. of decimal places;
         we don't bother to count the decimal point which takes up little room anyway */
      if (!(PlotArray.current % PlotArray.n) ||
          PlotMargin.right + 0.5 * (PlotSpace_i.x - PlotDim_i.x) >= hangover * font_width(Axis.font.size_i))
         x_tick = LonRange.max;
      else
         x_tick = LonRange.max - LonRange.step;
   }
   else
      x_tick = LonRange.max - (LonRange.max < 0 ? LonRange.step : 0) -
         frem(LonRange.max, LonRange.step);

   if (is_multiple(LatRange.max, LatRange.step))
      y_tick = LatRange.max;
   else
      y_tick = LatRange.max - (LatRange.max < 0 ? LatRange.step : 0) -
         frem(LatRange.max, LatRange.step);

   while (x_tick - LonRange.min >= -EPSILON)
   {
      if (is_multiple(x_tick, LonRange.step * Axis.label_interval.x))
      {
         /* Big x-ticks, top and bottom. */
         fprintf(FpPs, "%f 0.0 M 0.0 %f rlineto\n",
                 LonToIn(x_tick), big_tick);
         fprintf(FpPs, "%f %f M 0.0 %f neg rlineto\n",
                 LonToIn(x_tick), PlotDim_i.y, big_tick);

            /* Labels for bottom x-ticks. */
            x_tick_num = x_tick;        /* Number to print. */
            if (x_tick > 180.0)
               x_tick_num -= 360.0;  /* JR: was 360.0 - x_tick_num */
            nsew = ' ';
            if (x_tick_num > 0.0 && x_tick_num != 180.0)
               nsew = 'E';
            else if (x_tick_num < 0.0 && x_tick_num != -180.0)
               nsew = 'W';
            fprintf(FpPs, "(%.*f\312%c) %f %f SC\n", lon_decimal,
                    fabs(x_tick_num), nsew, LonToIn(x_tick),
                    x_tick_margin);
      }
      else
      {
         /* Small x-ticks, top and bottom. */
         fprintf(FpPs, "%f 0.0 M 0.0 %f R\n",
                 LonToIn(x_tick), small_tick);
         fprintf(FpPs, "%f %f M 0.0 %f  R\n",
                 LonToIn(x_tick), PlotDim_i.y, -small_tick);
      }
      x_tick -= LonRange.step;
   }

   while (y_tick - LatRange.min >= -EPSILON)
   {
      if (is_multiple(y_tick, LatRange.step * Axis.label_interval.y))
      {
         /* Big y-ticks, left and right. */
         fprintf(FpPs, "0.0 %f M %f 0.0 R\n",
                 LatToIn(y_tick), big_tick);
         fprintf(FpPs, "%f %f M %f 0.0 R\n",
                 PlotDim_i.x, LatToIn(y_tick), -big_tick);

         /* Labels for y-ticks. */
         if (y_tick != 0.0)
         {
            fprintf(FpPs, "(%.*f\312%c) %f %f SR\n",
                 lat_decimal, fabs(y_tick),
                 (y_tick < 0.0 ? 'S' : 'N'),
                 y_tick_margin,
                 LatToIn(y_tick) - VSPACE * Axis.font.size_i);
         }
         else
            fprintf(FpPs, "(EQ) %f %f SR\n", y_tick_margin,
                 LatToIn(y_tick) - VSPACE * Axis.font.size_i);
      }
      else
      {
         /* Small y-ticks, left and right. */
         fprintf(FpPs, "0.0 %f M %f 0.0 R\n",
                 LatToIn(y_tick), small_tick);
         fprintf(FpPs, "%f %f M %f 0.0 R\n",
                 PlotDim_i.x, LatToIn(y_tick), -small_tick);
      }

      y_tick -= LatRange.step;
   }
   fputs("stroke\n", FpPs);
}

#if PROTOTYPE_ALLOWED
void plot_labels(LABEL_TYPE * labelhead)
#else
void plot_labels(labelhead)
   LABEL_TYPE *labelhead;
#endif
{
   double lon, lat;
   LABEL_TYPE *label;
   double symbol_space, symbol_size_i;
   static char stroke_or_fill[2][15] = {"stroked_symbol", "filled_symbol"};
   /* center, left, right */
   static int just_direction[] = {0, 1, -1};
   static char show_string[3][3] = {"SC", "Sh", "SR"};

   label = labelhead;
   while (label)
   {
      lon = label->position.x;
      lat = label->position.y;

      if (NegativeLongitudes)
      {
         if (lon > 180.0)
            lon -= 360.0;
      }
      else
      {
         if (lon < 0.0)
            lon += 360.0;
      }

      symbol_space = 0.0;
      if (label->symbol)
      {
         symbol_size_i = label->symbol_size / DPI;
         fprintf(FpPs, "%s\n", stroke_or_fill[label->symbol & FILLED]);
         fprintf(FpPs, " %f %f %f %s\n", LonToIn(lon), LatToIn(lat),
                 symbol_size_i,
                 get_name(SymbolList, label->symbol & 0xFFFE));
         symbol_space = symbol_size_i;
      }
      if (label->text[0] != '\0')
      {
         label->font.size_i = label->font.size_p / DPI;
         fprintf(FpPs, " %f /%s Fsi\n", label->font.size_i, label->font.face);
         fprintf(FpPs, " (%s) %f %f %s\n", label->text,
                 LonToIn(lon) + just_direction[label->just] * symbol_space,
                 LatToIn(lat) - (label->font.size_i * 0.4)
                 - ((!just_direction[label->just])
                    * (symbol_space + (label->font.size_i * 0.6))),
                 show_string[label->just]);
      }

      label = label->next;
   }
}

void set_defaults()
{
   time_t lt;

   /*--- PLOT SCALE & PLACEMENT ---*/
   ShowPage = 1;                /* single plot per page  */
   NewPage = 1;         /* begin a new page */
   PlotArray.m = PlotArray.n = PlotArray.current = 1;   /* single plot per page */
   Axis.deg_per_inch.x = Axis.deg_per_inch.y = -1.0;    /* auto scaling */
   Axis.origin_i.x = Axis.origin_i.y = -1.0;    /* auto-center plot */
   strcpy(Orientation, "landscape");
   PageMargin.top = 1.0;       /* time stamp & annotation go here */
   PageMargin.bottom = 1.0;    /* default common legend will go here */
   PageMargin.left = 1.0;
   PageMargin.right = 0.5;

   /* 1999/07/13 Pierre Jaccard (A4) */
#ifndef DEFAULT_PAPER_SIZE
#define DEFAULT_PAPER_SIZE letter
#endif
#if DEFAULT_PAPER_SIZE == letter
   strcpy(PaperSize, "letter");
#else
#  if DEFAULT_PAPER_SIZE == a4
   strcpy(PaperSize, "a4");
#  else
   fprintf(stderr, "\n\n%s\n%s\n\n", 
           "WARNING: utility vector was compiled with an unsupported",
           "         value of DEFAULT_PAPER_SIZE. Using \"letter\".");
   strcpy(PaperSize, "letter");
#  endif
#endif

   /*--- VECTOR PLOTTING OPTIONS ---*/
   strcpy(VecFiles[0], "end");  /* no vectors/cruise_track data  */
   IndexUV = 0;                 /* no vectors */
   PlotCruiseTrack = 0;         /* no cruise track  */

   Arrow.cms_per_inch = 100;
   Arrow.headlength_i = 0.10;
   Arrow.min_headlength_i = -1.0;       /* defaults to half of Arrow.headlength_i later */
   Arrow.linewidth_p = 0.6;

   /*--- MAP PLOTTING OPTIONS ---*/
   Map.file[0] = '\0';                  /* no land outlines */
   Map.gray_level = 1.0;                        /* white = no shading */
   Map.linewidth_p = 0.4;

   /*--- AXES CONTROL ---*/
   LonRange.min = LonRange.max = 0.0;             /* nothing to plot */
   LatRange.min = LatRange.max = 0.0;             /* nothing to plot */
   Axis.linewidth_p = 0.5;                        /* pts default width */
   Axis.decimal_place.x = Axis.decimal_place.y = -1;  /* defaults to step * interval later */
   Axis.label_interval.x = Axis.label_interval.y = 1; /* label every tick mark */
   Axis.font.size_p = 10.0;
   strcpy(Axis.font.face, "Times-Bold");

   /*--- PLOT TITLES/ANNOTATIONS ---*/
   MainTitle.text[0] = '\0';    /* no main title */
   SubTitle.text[0] = '\0';     /* no subtitle */
   PlotTitle.text[0] = '\0';    /* no plot title */

   strcpy(TitleFont.face, "Times-Bold");
   TitleFont.size_p = 0.0;

   MainTitle.font.size_p = 20.0;
   SubTitle.font.size_p = 10.0;
   PlotTitle.font.size_p = 10.0;

   CommonLabelsHead = NULL;     /* no common labels */
   LocalLabelsHead = NULL;      /* no local labels */

   strcpy(Legend.type, "common");       /* single legend on entire page */
   Legend.pos.x = Legend.pos.y = -BADVALUE;  /* calculated later */
   strcpy(Legend.text, "Speed (cm/s)");
   strcpy(Legend.font.face, "Helvetica");
   Legend.font.size_p = 10.0;
   Legend.length = -BADVALUE;   /* defaults to Arrow.cms_per_inch later */
   Legend.linewidth_p = 0.5;

   time(&lt);
   strcpy(TodayDT, ctime(&lt));
   TodayDT[24] = '\0';          /* suppress linefeed */
   strcpy(TimeStamp, TodayDT);  /* today's date/time as returned by ctime */

   Annotation[0] = '\0';        /* no annotation */

   /*--- OUTPUT OPTIONS ---*/
   PsFile[0] = '\0';            /* output to stdout */
}
