[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[McIDAS #STZ-926779]: Request to add warning polygons to McIDAS WWDISP command



Hi Gilbert,

re:
> About a month ago you asked me if I would like to see these in the current
> version of McIDAS. I did say "no", but several things have made me
> reconsider that call:
> 
> 1. Recent severe weather events here. We're getting more of them,
> even though our season overall has been lame
> 
> 2. They really do show the area in danger a lot better than the
> whole county being shaded
> 
> 3. They update the polygons with severe weather statements (SVS's).
> 
> Is it too late to reconsider this decision?

No, not at all.

> If so, I understand, and I'll
> wait until next year.

The v2006 distribution of McIDAS should be released by the end of the
month (July).

> But if not, can you sneak it in, and make the
> updates plot with the SVS's? I didn't know they updated the polygons in
> the SVS's until recently, and that is a major plus that may have changed
> my mind in May had I known that.

I have attached the source code for the v2006 version of WWDISP for
you to try.  Here is the procedure to follow:

- rename the v2005 version of WWDISP's source code:

  <as 'mcidas'>
  cd ~mcidas/mcidas2005/src
  mv wwdisp.c wwdisp.c.2005

- extract the v2006 version of WWDISP source and copy it to
  your ~mcidas/mcidas2005/src directory.  Make sure that 'mcidas'
  owns the file, and it is called wwdisp.c.

- build and install the new WWDISP executable

  <as 'mcidas'>
  cd ~mcidas/mcidas2005/src
  make wwdisp.k
  rm ~/bin/wwdisp.k          <- assuming that the build went without problems
  ln wwdisp.k ~/bin
  make wwdisp.hlp
  rm ~/help/wwdisp.hlp       <- assuming that the build went without problems
  ln wwdisp.hlp ~/help

I have not tried to build a new WWDISP executable (wwdisp.k) in the v2005
src directory using the v2006 src, so be warned.  It may be the case that
one or more library files were changed to reimplement plotting of watch boxes.

> It's up to you.

If your test goes well, I will create a new addendum (v2005e) with the change
in case anyone else (COD) wants to get the boxes back.

> Thanks again for all your help, as always, and have a
> stellar 4th of July!

You too!

Cheers,

Tom
****************************************************************************
Unidata User Support                                    UCAR Unidata Program
(303) 497-8642                                                 P.O. Box 3000
address@hidden                                   Boulder, CO 80307
----------------------------------------------------------------------------
Unidata HomePage                       http://www.unidata.ucar.edu
****************************************************************************


Ticket Details
===================
Ticket ID: STZ-926779
Department: Support McIDAS
Priority: Normal
Status: Closed
/*
 * Copyright(c) 1997, Space Science and Engineering Center, UW-Madison
 * Refer to "McIDAS Software Acquisition and Distribution Policies"
 * in the file  mcidas/data/license.txt
 */


/**** $Id: wwdisp.c,v 1.73 2006/05/10 21:10:00 kevinb Tst $ ****/


/*
*? WWDISP - Displays active weather warnings, watches and advisories
*?    WWDISP map frame <keywords>
*? Parameters:
*?    map | map for the plot; use any predefined map in the MAP command,
*?          e.g., USA, MID, KS (def=use navigation of frame specified in
*?          the NAV keyword; no map is drawn)
*?    frame | frame number to display the output (def=current)
*? Keywords:
*?    COLor=tor svr toroutl svroutl win winoutl snowadv bliz blizoutl fldwarn 
fldwatch
*?          graphics color levels of output (def=2 3 5 4 14 10 11 7 8 6 9 for 
*?          sessions with more than 8 color levels; def=2 3 5 4 6 2 4 7 8 6 1 
*?          for sessions with 8 or less color levels), where:
*?             tor - color of tornado watch outlines and warning fill
*?             svr - color of severe thunderstorm watch outlines and
*?                   warning fill
*?             toroutl - color of county outlines drawn for tornado warnings
*?             svroutl - color of county outlines drawn for severe
*?                       thunderstorm warnings
*?             win - color of winter storm watch outlines and warning fill
*?             winoutl - color of zone outlines drawn for winter storm warnings
*?             snowadv - color of zone outlines drawn for snow advisories
*?             bliz - color of zone fill drawn for blizzard warnings
*?             blizoutl - color of zone outlines drawn for blizzard warnings
*?             fldwarn - color of flood warning outlines and fill
*?             fldwatch - color of flood watch outlines and fill
*?    DAY=bday eday | range of days to plot active bulletins (def=today bday)
*?    FILl=  | filling of the zones and counties under a severe thunderstorm, 
*?             tornado, winter storm, or blizzard warning; uses the colors 
*?             specified for 'svr', 'tor', 'win', and 'bliz' in the
*?             COLOR keyword; choose one of the following options (def=SOLID):
*?         =NONE | do not fill warning counties
*?         =SOLID | completely fill the warning counties with solid color
*?         =TRAN density | partially fill to give a transparent effect; density
*?                         range is 2-10; a density value of n sets pixel 
*?                         interval so every nth pixel is filled; larger values
*?                         give a greater transparent effect (density def=4)
*?    GROup= | ADDE group name to search; the group name must be mapped to a
*?             server with weather text data (def=RTWXTEXT)
*?    NAV= | frame to use for navigation; specify a frame number or C for
*?           current frame (def=C); no map is drawn; you cannot use this
*?           keyword unless the default is used for the map parameter
*?    PLOt=BOX | plot severe thunderstorm and tornado watches as box outlines
*?        =COUNTY | plot watches as county outlines
*?        =NBOX height | plot watches as box outlines with watch number in
*?                       center of each box using specified pixel height
*?                       (def=NBOX 7)
*?    TIMe=btime etime | range of times to plot active bulletins
*?                       (def=current btime)
*?    TYPe=t1..tn | bulletin type(s) to plot; you cannot use TYPE with the
*?                  WATCH keyword; valid TYPEs are SEVWARN,SEVWATCH,TORWARN,
*?                  TORWATCH,WINWARN,WINWATCH,BLIZZARD,SNOWADV,FLOODWARN,
*?                  FLOODWATCH,ALL (def=ALL),
*?                  where:
*?                     SEVWARN, SEVWATCH=severe thunderstorm warnings, watches
*?                     TORWARN, TORWATCH=tornado warnings, watches
*?                     WINWARN, WINWATCH=winter storm warnings, watches
*?                     BLIZZARD=blizzard warnings, SNOWADV=snow advisories
*?                     FLOODWARN, FLOODWATCH=flood/flash flood warnings, watches
*?                     ALL=all of the above
*?    WATch=num1..numn | severe thunderstorm and tornado watch numbers to plot;
*?                       the watches are plotted only if they are active during
*?                       the time period specified with DAY and TIME keywords
*?                       (def=all active watches during time period specified
*?                       with DAY&TIME); you cannot use WATCH with TYPE keyword
*?    WIDth=width | graphics line width of county outlines and watch boxes
*?                  (def=current GD command setting)
*?    WLIst=YES | plot watch list in upper-left of screen; the list includes
*?                the watch number, day of month and time it expires (def)
*?         =NO  | do not plot watch list
*     DTIME= | max number of hours back from btime to search for severe
*              weather events (default=16)
*     EXPIRED= NO              | Do not display expired events (default)
*              YES outl        | Display expired events with DASHed outlines
*                              | or SOLID outlines.  outl may be DASH or SOLID
*? Remarks:
*?    If both watches and warnings are active, the watches are plotted before
*?    the warnings, regardless of which were issued first. The map is drawn
*?    after all the watches and warnings are plotted.
*?
*?    Severe thunderstorm and tornado watches are plotted as either a box or 
*?    county outlines. The watch number prefaced by a T (tornado) or 
*?    an S (severe thunderstorm) is plotted in the center of each box when 
*?    PLOT=NBOX (the default) is used. Winter storm watches are always plotted
*?    as zone outlines.
*?
*?    Watches are outlined in the tor, svr and win colors specified with the 
*?    COLOR keyword. Counties and zones under a warning are filled with the 
*?    tor, svr, win and bliz colors using the shading specified with the FILL 
*?    keyword, and are outlined with the toroutl, svroutl, winoutl and blizoutl
*?    colors specified in the COLOR keyword. Flood/flash flood watches and
*?    warnings, and snow advisories are filled with the fldwatch, fldwarn and
*?    snoadv colors in a hatch pattern unless FILL=NONE is specified.
*?
*?    If a range is specified with the DAY and TIME keywords, bulletins that
*?    are active in that time window are plotted. For example, DAY=185 187
*?    TIME=21 3 specifies a window from 21:00 UTC on day 185 through 03:00 UTC
*?    on day 187. If a single day and time is specified, bulletins that are
*?    active at that day and time are plotted. The default is the current day
*?    and time.
*?
*?    Tornado and severe thunderstorm watches that have been cancelled are
*?    considered expired and are not displayed. Winter storm watches and all
*?    warning events that have been cancelled are displayed until their normal 
*?    expiration time has passed.
*
*     The DTIME keyword is used to specify how many hours back to look
*     for the issuance of severe weather events.  This keyword is use-
*     ful when plotting expired events, because it specifies how far back
*     in time to look for and plot events.  The default is 16 hours back
*     from btime.  DTIME can be set to a minimum of 1 hour.
*? ----------
*/

/*
** Before we get into this keyin I will describe a bit of how it works
** on the front end so someone will have a bit of a clue going in
** what is done.
**
** The major steps this baby performs are as follows:
**
**    Initialize McIDAS environent.
**    Get the workstation attributes.
**      this is needed because the values of the workstation attributes
**      are used to set default values and ranges for several of the
**      keywords available to this command.
**    Get the user entered options for parameters and keywords.
**    If necessary, setup navigation on the frame
**    Build a vector of requests to send to the server.  This is necessary
**      because county watches, and warnings are retrieved by the APRO
**      products SLS, SVR, and TOR.  Watch box points and watch box cancel-
**      lations are retrieved by the WMO product WWUS9.  Both a APRO and
**      a WMO request can not be sent to the server in one connection.
**    Read the text data from the server.  If the text is a watch, determine
**      if it is a cancellation and save the watch number for later use.
**      Put the text data on a stack. The text is put on a stack
**      because the server is going to send the data back to the client
**      from the newest report back in time to the oldest report. But for this
**      plotting command we would rather plot from the oldest report to the
**      newest report. So a stack is built to perform the necessary
**      LIFO processing.
**      There are actually 4 stacks, one for each of the event types
**      that can be plotted. The reason for 4 is that we draw the watches
**      first then the warnings. This way the county outlines are not
**      destroyed if a watch comes in after a warning for the same county.
**    Process the text reports.
**      Pop a text report from the stack.
**      If the report is a watch decode the watch number and see if this watch
**        number is in our list of cancelled watches.  If yes, adjust the
**        expiration time of the watch to the issue time of the cancellation.
**      Break the report into a list of county/state pairs and expiration times
**        for each county.  If the user wants to plot a watch as a box, then
**        decode the box points from the WWUS9 report.
**      Get the list of lat/lon pairs for each active county.
**      Draw the county outline to the screen or the watch box points.
**    If necessary, draw map over county outlines.
*/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "mcidas.h"
#include "mcidasp.h"
#include "wtxmisc.h"
#include "m0wwmisc.h"

#define   FALSE         0
#define   TRUE          1

#define   NUMFILLTYPES  3
#define   OPEN          0
#define   SOLID         1
#define   DITHER        2
#define   DASH          3
#define   HATCH         4

/*
 * The following three declarations are no longer needed. As of now,
 * the State/County/Zone database and it's associated API (found
 * in Mcsczgtc.c) are used to obtain county/zone lat/lon pairs.
 * These variables are declared to satisfy the extern declarations
 * found in wwmisc.c
 */
const char  *CountyFile = {"CNTYOUTL.DAT"};
                                             /* this is the name of the
                                              * file containing the lat/lon
                                              * pairs for each county in the
                                              * US. this field is a global
                                              * variable because it is
                                              * a constant and there are
                                              * three different routines
                                              * in this source that need
                                              * the file name */
Fint         CountyFileHeader[16];           /* this is the file header
                                              * for the county outline
                                              * file */
FsLen        CountyFileLength;               /* length of the county file
                                              * name. this is necessary
                                              * because the county data
                                              * API routines are in
                                              * fortran */
int          DebugFlag;                      /* bit-mask flag that can be
                                              * used to get more debug
                                              * information than any human
                                              * could possibly want.
                                              * bit  description
                                              *   0  decoded fips list
                                              *   1  lat/lon pairs
                                                          *   2  frame lat/lon 
extents
                                              *   3  lat/lon line/element
                                              *   4  actual observation
                                              *   5  ignore valid time
                                              *   6  cancellation list
                                              */


typedef struct WATCHNODE_
{
        int     type;
        int     number;
        int     cntr_line;
        int     cntr_elem;
        int     beg_date;
        int     beg_time;
        int     end_date;
        int     end_time;
        struct WATCHNODE_       *left;
        struct WATCHNODE_       *right;
} WATCHTREE;

/* - Function Prototypes ------------------------------------ */

static int
  AddToWatchTree(WATCHTREE **, int, int, int, int, int, int, int, int);

static void
  TraverseWatchTree(WATCHTREE *, void (*)(int, int, int, int, int, int, int, 
int,
                    int*, int *), int *, int *);

#if 0
static int
  GetCountyLatLonPairs (int *, double *, double *);
#endif

static int
  GetColorLevels ( int *, int);

static int
  GetFillType(int *);

static int
  GetFrameMinMax(int, double *, double *, double *, double *);

static void
  DrawTitles (int, int, int, int, int, int, int, int, int *, int *, int, int);

static void
  DrawWatchBoxList (int, int, int, int, int, int, int, int, int *, int *);

static int
  GetWatchBoxCenter(int,  double *, double *, int, int *, int *);

static void
  DrawWatchBoxNumber(int, int, int, int, int, int, int, int, int *, int *);

static int
  NumPolygon (int, double *, double *, int **, int **);

static int
  GetNavFrameNumber (const char *, int, int, int *, int);

static void
  GetPlotColor (int, int *, int *, int *);

static void
  PrintLatLons (int, double *, double *);

static int
  M0DrawLatLonPairs ( int, double *, double *, int, int, int, int, int);

static int
  M0DrawMap (int, const char *, const char *, int *);

static int
  M0SetMapNav (int, int, const char *, const char *, int *);

extern void
  mapsav_ (Fint *, float *, Fint *, const char *, float *, FsLen);

extern Fint
  frtonv_
    (Fint *, Fint *);

extern void
  nvtofr_ (Fint *, Fint *);

extern Fint
  u0getftime_ (Fint *, Fint *, Fint *);   /* <<<<< UPC add 980414 >>>>> */

extern Fint
  u0putftime_ (Fint *, Fint *, Fint *);   /* <<<<< UPC add 980414 >>>>> */


/* - Main -------------------------------------------------------- */

int main (int argc, char **argv)
{
  int                  max_line_number;      /* highest line number, for
                                              * the frame that will be
                                              * written to */
  int                  max_element_number;   /* highest element number */
  int                  independent_graphics; /* flag indicating if the
                                              * workstation has
                                              * independent graphics */
  int                  sat_projection_used;  /* flag indicating that the
                                              * user wants the graphic
                                              * plotted on a SAT projection */
  int                  max_graphics_frame;   /* max graphics frame for
                                              * this session */
  int                  image_frame_on;       /* flag indicating if image
                                              * is turned on */
  int                  current_image_frame;  /* image frame workstation
                                              * is on */
  int                  current_graphics_frame; /* graphics frame currently
                                              * on */
  int                  max_color_levels;     /* max color levels for this
                                              * workstation */
  int                  output_frame_number;  /* graphics frame number that
                                              * will actually be written
                                              * to */
  int                  using_image_nav;      /* flag indicating whether
                                              * image nav should be used */
  int                  nav_frame_number;     /* frame number to get
                                              * navigation from */
  int                  screen_domain[4];     /* min and max line and element
                                              * to use */
  int                  selectStringIndex;    /* index for current select
                                                                  * to send to 
server */
  int                  color_levs[MAX_EVENTS]; /* color levels for events */

  int                  ditherFactor;         /* Factor used to dither watch
                                              * boxes */
  int                  width;                /* graphic width of outlines */
  int                  fill_type = OPEN;     /* type of fill to use when
                                              * filling counties for warnings*/
  int                  warning_fill_type;    /* how to plot warning fills */
  int                  ok;                   /* function return value */
  int                  i;                    /* loop counter */
  int                  j;                    /* another loop counter */
  int                  return_val = 1;       /* main() return value */
  int                  current_day;          /* today's date */
  int                  current_time;         /* today's time */
  int                  current_secs;         /* today's seconds since epoch */
  int                  beg_day;              /* beginning day of range */
  int                  beg_time;             /* beginning time of range */
  int                  beg_secs;             /* beginning seconds since epoch */
  int                  end_day;              /* ending day of range */
  int                  end_time;             /* ending time of range */
  int                  end_secs;             /* ending seconds since epoch */
  int                  dtime;                /* number of hours back from end
                                              * time to get from server */
  int                  error_return;         /* error flag return value */
  int                  n_pairs = 0;          /* number of lat/lon pairs */
  int                  n_selects;            /* number of selection
                                              * parameters specified */
  int                  n_zones;              /* number of zones in decoded
                                              * text block */
  int                  user_dtime;           /* dtime set by user */
  int                  events_array[MAX_EVENTS]; /* array of flags for each 
event type */
  int                  watch_number;         /* current watch number */
  static int           watchlist[MAXWATCHLIST];/* list of watch box numbers
                                              * the user wants plotted */
  int                  indexWatchList;       /* index returned from InWatchList 
function */
  int                  plot_watch_number;    /* boolean to indicate if user
                                              * wants the watch number plotted
                                              * inside of box */
  int                  plot_watch_list;      /* boolean to indicate if user
                                              * wants to plot the list of
                                              * watches on the graphics */
  WATCHTREE            *watchsave=NULL;      /* Tree that will contain info
                                              * about all watches for use
                                              * at the end of program */
  int                  cntr_line;
  int                  cntr_elem;
  static int           n_watch_num_draw=0;   /* number of watches to place text
                                              * in center */
  static int           watch_num_draw_height;  /* height at which to plot watch
                                              * numbers */
  int                  numwatchlist;         /* number of watches in watchlist
                                              * array */
  static int           beg_line[MAX_ZONES];  /* beginning text line number
                                              * for each zone */
  static int           end_line[MAX_ZONES];  /* ending text line number
                                              * for each zone */
                                             /* text index header */
  static int           fips_num[MAX_ZONES];  /* list of fips numbers decoded */
  static int           valid_day[MAX_ZONES]; /* list of valid days for each
                                              * zone */
  static int           valid_time[MAX_ZONES];/* list of valid times for
                                              * each zone */
                                             /* adde weather text header
                                              * returned with each call to
                                              *M0wtxread () */
  int                  text_header[IDX_HEADER_SIZE];
  int                  expired;              /* boolean to say if event has
                                              * expired */
  int                  orig_dash_len;        /* dash length before program */
  int                  orig_gap_len;         /* gap length before program */
  int                  orig_gap_color;       /* gap color before program */
  int                  plot_color;           /* color to plot each event in */
  int                  plot_expired;         /* boolean to say if we should
                                              * expired watches and warnings */
  int                  at_least_one = FALSE; /* flag indicating that at
                                              * least one active county
                                              * was found */
  int                  watch_disp_type;      /* display watches as boxes
                                              * or counties. */
  WWSTACK             *stack[MAX_EVENTS];    /* the beginning of the data
                                              * stack */

  static double        lats[MAX_LATLON];     /* list of latitudes for a
                                              * county outline */
  static double        lons[MAX_LATLON];     /* list of longitudes for a
                                              * county outline */

  const char          *valid_keywords[] = {  /* list of valid keywords */
                          "COL.OR",
                          "DAY",
                          "DEB.UG",
                          "DT.IME",
                          "EXP.IRED",
                          "FIL.L",
                          "GRO.UP",
                          "NAV",
                          "OLD",
                          "TIM.E",
                          "TYP.E",
                          "PLO.T",
                          "WAT.CH",
                          "WID.TH",
                          "WL.IST"};
  int                  n_valid_keywords =    /* number of valid keywords */
                         (sizeof (valid_keywords) /
                          sizeof (valid_keywords[0]));

  char               **selection_strings = NULL; /* list of search selection
                                                  * strings */
  char               **state;                /* list of valid states for
                                              * each zone */
  char               **fips_type;            /* list of fips id types */
  const char          *mapbase;              /* base map to use */
  const char          *projection;           /* projection of map */
  char                *group;                /* adde group name */
  char                **prod_string;         /* vector of APRO= or WMO= that
                                              * will be passed to server */
  const char          *watch_disp_sel;       /* display watches as boxes
                                              * or counties. */
  const char          *dash_selection;       /* expired keyword for dash or
                                                solid display of expired evnt */
  char                *error_string;         /* error string */
  char                *text_data;            /* buffer to contain the
                                              * resulting text data */
  char                 t_string[40];         /* temporary string used to
                                              * build adde request */
  char                *ctemp;                /* temporary string pointer */

  double                min_lat;             /* minimum lat of frame */
  double                max_lat;             /* maximum lat of frame */
  double                min_lon;             /* miminum lon of frame */
  double                max_lon;             /* maximum lon of frame */

  short int         mapSet = FALSE;       /* only initialize map once */

  Freal                 tlat;
  Freal                 tlon;
  int                   dum[3];
  static int            tvline;
  static int            tvelem;
  char                  *segment_text[MAX_SEGMENTS];
  int                   *segment_header[MAX_SEGMENTS];
  int                   n_segs = 0;

  EVENT                 event;
  char                  *vtecs[MAX_VTECS];
  int                   num_vtecs;

  static int           num_cancels[MAX_EVENTS];/* number of cancellation for
                                              * each of the events */
  static int           cancel_number[MAX_EVENTS][MAX_CANCELS];
  static int        cancel_secs[MAX_EVENTS][MAX_CANCELS];

  /*
   * you will notice that I perform one of the great C programming
   * taboos by using a goto/label statement. the reason I did this
   * was so that I only had to put in one Mceprintf ("done") call
   * if someone really wants to complain I can change this, but it
   * just seemed like an easy way to handle getting may possible
   * failures to the same location.
   */

  /* initialize mcidas environment */

  ok = Mcinit (argc, argv);
  if (ok < 0)
  {
    fprintf (stderr, "%s\n", Mciniterr ());
    goto End_Of_Main;
  }

  /* initiliaze event stacks */
  for ( i=0 ; i < MAX_EVENTS ; i++ ) stack[i] = (WWSTACK *)NULL;

  /* get current dash settings - need to return values upon completion */

  McGetDashAttributes(&orig_dash_len, &orig_gap_len, &orig_gap_color);

  /* get the graphic width */
  ok = Mccmdint ("WID.TH", 1, "Graphic Width", McGetGraphicWidth(), 0, 10, 
&width);
  if (ok < 0) { goto End_Of_Main; }

  /* get the debug bit mask */

  ok = Mccmdint ("DEB.UG", 1, "Debug Flag", 0, 99, -99, &DebugFlag);

  /* validate the keyword list */

  ok = Mccmdkey (n_valid_keywords, valid_keywords);
  if (ok != 0) { goto End_Of_Main; }

  ok = Mccmdint("DT.IME", 1, "Hours to look back", 16, 1, 720, &user_dtime);
  if (ok < 0) { goto End_Of_Main; }

  /* get the current day and time */

  (void) Mcgetdaytime (&current_day, &current_time);
  ok = Mcdaytimetosec (current_day, current_time, &current_secs);

  /* get the day and time range from the user */

  ok = Mccmdiyd ("DAY", 1, "Begin day", current_day, 1, -1, &beg_day);
  if (ok < 0) { goto End_Of_Main; }

  ok = Mccmdiyd ("DAY", 2, "End day", beg_day, 1, -1, &end_day);
  if (ok < 0) { goto End_Of_Main; }

  ok = Mccmdihr ("TIM.E", 1, "Begin time", current_time, 0, 235959, &beg_time);
  if (ok < 0) { goto End_Of_Main; }

  ok = Mccmdihr ("TIM.E", 2, "End time", beg_time, 0, 235959, &end_time);
  if (ok < 0) { goto End_Of_Main; }

  ok = Mcdaytimetosec (beg_day, beg_time, &beg_secs);

  ok = Mcdaytimetosec (end_day, end_time, &end_secs);

  if (end_secs < beg_secs)
  {
      Mceprintf("Begin time is greater than end time\n");
      goto End_Of_Main;
  }

  /* Compute the number of hours to retrieve from server */

  dtime = ((end_secs - beg_secs) / 3600) + user_dtime;
  if (end_day != current_day && end_time != current_time )
      dtime = dtime + (24 - end_time / 10000);

  if (end_day == current_day && end_time != current_time )
      dtime = dtime + current_time / 10000;

  /* Get the list of watches the user wants plotted */
  numwatchlist = Mccmdnum ("WAT.CH");
  if (numwatchlist > 0)
  {
      if (numwatchlist > MAXWATCHLIST) numwatchlist = MAXWATCHLIST;

      for (i=1; i<=numwatchlist; i++)
      {
          ok = Mccmdint("WAT.CH", i, "Watch Number", 0, 0, 10000, 
&watchlist[i-1]);
      }
  }

  Mcdprintf("User specified %d watches to plot\n", numwatchlist);
  for (i=0; i<numwatchlist; i++)
  {
      Mcdprintf ("   watch number %d\n", watchlist[i]);
  }

  /* get the ADDE group name */

  ok = Mccmdstr ("GRO.UP", 1, "RTWXTEXT", (const char **) &group);
  if (ok < 0) { goto End_Of_Main; }

  /*
   * let's get all of the terminal attributes we need up front
   * so we don't have to grab them later. we need them anyhow to
   * set defaults for some of the keywords
   */

  independent_graphics     = Mctermch ("IND_GRAPHIC?");
  max_color_levels         = Mctermch ("MAX_COL_LEV");
  max_graphics_frame       = McGetMaxGraphicsFrameNumber ();
  current_image_frame      = McGetImageFrameNumber ();
  current_graphics_frame   = McGetGraphicsFrameNumber ();
  nav_frame_number         = current_graphics_frame;
  image_frame_on           = McIsImageFrameOn ();

  /* get the type of outline to use for watches */
  ok = Mccmdstr ("PLO.T", 1, "NBOX", (const char **) &watch_disp_sel);

  if (ok < 0)
  {
    Mcprintf ("Unable to retrieve watch form %d\n", ok);
    goto End_Of_Main;
  }

  plot_watch_number = FALSE;

  if (watch_disp_sel[0] == 'C')
  {
        watch_disp_type = WATCH_CTY;
  }
  else if (watch_disp_sel[0] == 'B')
  {
        watch_disp_type = WATCH_BOX;
  }
  else if (watch_disp_sel[0] == 'N')
  {
        watch_disp_type = WATCH_BOX;
        plot_watch_number = TRUE;
        ok = Mccmdint ("PLO.T", 2, "Watch Number Plot Height", 7, 1, 100,
                        &watch_num_draw_height);
  }
  else
  {
      Mceprintf("Unknown watch display format selected\n");
      goto End_Of_Main;
  }

  plot_watch_list = FALSE;
  ok = Mccmdstr ("WL.IST", 1, "YES", (const char **) &ctemp);

  if (ok < 0) { goto End_Of_Main; }

  if (ctemp[0] == 'Y' || ctemp[0] == 'y') plot_watch_list = TRUE;

  /* get the map base to use */

  ok = Mccmdstr ("", 1, "SAT", (const char **) &mapbase);

  if (ok < 0)
  {
    Mcprintf ("Unable to retrieve map name %d\n", ok);
    goto End_Of_Main;
  }

  /* Allow use of OLAY as map - set to SAT */
  if (strcmp (mapbase, "OLAY") == 0) mapbase = "SAT";

  /* set a flag indicating whether the SAT projection is requested */

  sat_projection_used = FALSE;
  if (strcmp (mapbase, "SAT") == 0)
  {
    sat_projection_used = TRUE;
    projection = strdup ("SAT");
  }

  if (sat_projection_used == FALSE)
  {
    ok = Mccmdstr ("PRO", 1, "MERC", &projection);
    if (ok < 0)
    {
      Mcprintf ("Unable to retrieve map projection %d\n", ok);
      goto End_Of_Main;
    }
  }

  /*
   * now determine if we are to use image navigation. we do this here
   * because this will determine what our default is for the GRA=
   * value on machines that have independent graphics frames
   */

  using_image_nav = FALSE;
  if ((sat_projection_used == TRUE) && (image_frame_on == TRUE))
  {
    using_image_nav = TRUE;
  }

  /* use the current graphics frame number for output unless otherwise 
   * specified */

  ok = Mccmdint ("", 2, "Graphics frame", current_graphics_frame,
                 1, max_graphics_frame, &output_frame_number);
  if (ok < 0) { goto End_Of_Main; }

  /* get the frame number to use for navigation. */
  ok = GetNavFrameNumber (mapbase, output_frame_number, image_frame_on,
                          &nav_frame_number, max_graphics_frame);

  if (ok < 0)
  {
    Mceprintf("Error in GetNavFrameNumber\n");
    goto End_Of_Main;
  }

  /* Allow user to specify TIME=FRAME  or TIME=LATEST
                          <<<<< UPC mod 980414 - Begin >>>>> */
  /* Moved time selection keyword pickup to after the frame pickup */

  ok = Mccmdint("DT.IME", 1, "Hours to look back", 16, 1, 720, &user_dtime);
  if (ok < 0)
  {

    goto End_Of_Main;
  }

  /* get the current day and time */

  (void) Mcgetdaytime (&current_day, &current_time);
  ok = Mcdaytimetosec (current_day, current_time, &current_secs);

  /* get the day and time range from the user */

  ok = Mccmdstr ("TIM.E",  1, "", (const char **) &ctemp);
  if (ok < 0) {
      goto End_Of_Main;
  }

  if (strcmp (ctemp, "FRAME") == 0) {
     int frame_day, frame_time;
     ok = u0getftime_(&nav_frame_number, &frame_day, &frame_time);
     Mcdprintf ("Frame for time         %d\n", nav_frame_number);
     Mcdprintf ("Frame day              %d\n", frame_day);
     Mcdprintf ("Frame time             %d\n", frame_time);
     if (ok < 0) {
        beg_day = current_day;
        beg_time = current_time;
     } else {
        beg_day = frame_day;
        beg_time = frame_time;
     }
     end_day = beg_day;
     end_time = beg_time;

  } else if (strcmp (ctemp, "LATEST") == 0) {

     beg_day = current_day;
     beg_time = current_time;
     end_day = beg_day;
     end_time = beg_time;

  } else {

     ok = Mccmdiyd ("DAY", 1, "Begin day", current_day, 1, -1, &beg_day);
     if (ok < 0) {
         goto End_Of_Main;
     }
     ok = Mccmdiyd ("DAY", 2, "End day", beg_day, 1, -1, &end_day);
     if (ok < 0) {
         goto End_Of_Main;
     }
     ok = Mccmdihr ("TIM.E", 1, "Begin time", current_time, 0, 235959, &beg_time
);
     if (ok < 0) {
         goto End_Of_Main;
     }
     ok = Mccmdihr ("TIM.E", 2, "End time", beg_time, 0, 235959, &end_time);
     if (ok < 0) {
         goto End_Of_Main;
     }

  }

  ok = Mcdaytimetosec (beg_day, beg_time, &beg_secs);
  ok = Mcdaytimetosec (end_day, end_time, &end_secs);

  if (end_secs < beg_secs) {
      Mceprintf("Begin time is greater than end time\n");
      goto End_Of_Main;
  }

  /* Compute the number of hours to retrieve from server */

  dtime = ((end_secs - beg_secs) / 3600) + user_dtime;
  if (end_day != current_day && end_time != current_time )
      dtime = dtime + (24 - end_time / 10000);

  if (end_day == current_day && end_time != current_time )
      dtime = dtime + current_time / 10000;

  /* <<<<< UPC mod 980414 - End >>>>> */

  /* get the screen size */

  ok = Mcfsize (output_frame_number, &max_line_number, &max_element_number);

  /*
   * get the screen size the user wants to use. the defaults and ranges
   * for these values will vary depending on what the screen dimension
   * is and what the user enters for previous coordinates. the defaults
   * for these four values are:
   *
   *          default         minimum value             maximum value
   *  top       20                  1                       100
   *  bottom  (max line-60)      top+200                  (max line-60)
   *  left      20                  1                       100
   *  right   (max ele-20)      left+200                 max elem
   *
   *  The ratios used in the equations below are used by MAP to determine 
   *  the page borders for frames bigger than 480 X 640.
   */

  /* Top line */
  screen_domain[0] = (int)(0.0408 * (float)max_line_number) + 1;
  if( screen_domain[0] < 20 ) screen_domain[0] = 20;

  /* Bottom Line */
  screen_domain[1] = (int)(0.8805 * (float)max_line_number );
  if( screen_domain[1] > (max_line_number-60) ) screen_domain[1] = 
max_line_number - 60;

  /* Left Element */
  screen_domain[2] = (int)(0.0313 * (float)max_element_number ) +1; 
  if( screen_domain[2] < 20 ) screen_domain[2] = 20;

  /* Right Element */
  screen_domain[3] = (int)(0.9688 * (float)max_element_number );
  if( screen_domain[3] > (max_element_number-20) ) screen_domain[3] = 
max_element_number - 20;

  /* get the color level information */
  ok = GetColorLevels (color_levs, max_color_levels);
  if (ok < 0) { goto End_Of_Main; }

  /* get the way the user wants the warning counties filled */
  Mcdprintf("Calling GetFillType()\n");
  ok = GetFillType(&warning_fill_type);
  if (ok < 0) { goto End_Of_Main; }

  if (warning_fill_type == DITHER)
  {
      ok = Mccmdint("FIL.L", 2, "Dither Spacing", 4, 2, 10, &ditherFactor);
      if(ok < 0) { goto End_Of_Main; }
  }

  ok = Mccmdstr ("EXP.IRED", 1, "NO", (const char **)&dash_selection);
  if (ok < 0) { goto End_Of_Main; }

  /* see how the user wants to plot expired watches and warnings */
  plot_expired = FALSE;
  if (dash_selection[0] != 'N') plot_expired = TRUE;

  if (plot_expired)
  {
      ok = Mccmdstr ("EXP.IRED", 2, "DASH", (const char **)&dash_selection);
      if (ok < 0) { goto End_Of_Main; }

      if (dash_selection[0] == 'D')
      {
        plot_expired = DASH;
      }
      else if(dash_selection[0] == 'S')
      {
        plot_expired = SOLID;
      }
      else 
      {
          Mceprintf("Invalid selection for EXPIRED keyword\n");
          goto End_Of_Main;
      }
  }

  /* print out debug messages about workstation attributes */

  Mcdprintf ("Current image and graphics frames %d %d\n",
              current_image_frame, current_graphics_frame);
  Mcdprintf ("Image frame on?       %d\n", image_frame_on);
  Mcdprintf ("Independent graphics? %d\n", independent_graphics);
  Mcdprintf ("Using image nav?      %d\n", using_image_nav);
  Mcdprintf ("SAT projection used?  %d\n", sat_projection_used);
  Mcdprintf ("Output to graphics frame number %d\n", output_frame_number);
  Mcdprintf ("Navigation frame number %d\n", nav_frame_number);
  Mcdprintf ("Screen dimensions: line 1 x %d  element 1 x %d\n",
             max_line_number, max_element_number);
  Mcdprintf ("Plot dimensions: line %d x %d  element %d x %d\n",
             screen_domain[0], screen_domain[1],
             screen_domain[2], screen_domain[3]);
  Mcdprintf ("Warning county warning_fill_type=%d\n", warning_fill_type);
  Mcdprintf ("Max color levels %d\n", max_color_levels);
  Mcdprintf ("Max graphics frame %d\n\n", max_graphics_frame);

  /*
   * get the information for the TYPE= values. this is done in a function
   * because we need to build a APRO= request string based on what
   * the user enters and we have to check to see what types of watches
   * the user requests. GetEventTypeString will print out its own error
   * messages.
   */
  prod_string = VecNew();
  if(prod_string == (char **)NULL)
  {
      Mceprintf("Internal malloc error on prod_string\n");
      goto End_Of_Main;
  }

  ok = GetEventTypeString (watch_disp_type, &prod_string, events_array);
  if (ok < 0)
  {
    Mcdprintf ("GetEventTypeString returns %d\n", ok);
    goto End_Of_Main;
  }

  Mcdprintf("List of request strings:\n");
  for(i=0; i<VecLen(prod_string); i++)
  {
      Mcdprintf ("    prod_string[%d]: %s\n", i, prod_string[i]);
  }

  /*
   * if we make it to here we will be looking for a text match of some kind
   */

  /*
   * Get the selection conditions. The only selection the user has
   * control over is the APRO= or WMO= products that is desired.
   * Tornado warnings are TOR products, severe thunderstorm warnings
   * are SVR products and Tornado and Severe thunderstorm watches
   * are SLS products. If the user wants watch boxes, then we get
   * WMO=WWUS9 products. Otherwise, the request will be, get all
   * products issued in the last 16 hours.
   */

  Mcdprintf("\n********************************************************\n");
  Mcdprintf("*** Start reading data and pushing it onto the stack ***\n");
  Mcdprintf("********************************************************\n\n");

  for (selectStringIndex=0;
       selectStringIndex < VecLen(prod_string);
       selectStringIndex++)
  {
    /*char * beginTimeString;
    char * endTimeString;*/
    selection_strings = VecNew();

    /*if (beg_day != end_day)
       sprintf (t_string, "DAY=%d %d", beg_day,end_day);
    else*/
       sprintf (t_string, "DAY=%d",end_day);

    selection_strings = VecAdd (selection_strings, t_string);
    /*if (beg_time != end_time)
    {
      ok = Mchmstostrform (beg_time, "(hh):(mm):(ss)", &beginTimeString);
      ok = Mchmstostrform (end_time, "(hh):(mm):(ss)", &endTimeString);
      sprintf (t_string, "TIME=%s %s", beginTimeString,endTimeString);
      selection_strings = VecAdd (selection_strings, t_string);
    }*/
    selection_strings = VecAdd (selection_strings, 
prod_string[selectStringIndex]);
    sprintf (t_string, "DTIME=%d", dtime);
    selection_strings = VecAdd (selection_strings, t_string);
    selection_strings = VecAdd (selection_strings, "NUM=9999");
    n_selects         = VecLen (selection_strings);

    Mcdprintf ("Request will go to %s\n", group);
    for (i = 0 ; i < n_selects ; i++)
    {
      Mcdprintf ("  %s\n", selection_strings[i]);
    }

    /* send the request off to the server */
    ok = M0wtxget (group, n_selects, selection_strings, 1);
    if (ok < 0)
    {
      error_return = M0WxTextErrorString (ok, &error_string);
      if (error_return >= 0)
      {
        Mcdprintf ("%s\n", error_string);
        free (error_string);
      }
      Mcdprintf ("M0wtxget returns %d\n", ok);
      goto End_Of_Main;
    }

    VecOld (selection_strings);

    /*
     * if we make it to here we have actual data waiting to be sent back
     * from the server and the county outline file was found. let's
     * initialize navigation.
     */

      /* initialize map -> graphics navigation */

    if (! mapSet)
    {
      ok = M0SetMapNav (nav_frame_number, output_frame_number,
                        mapbase, projection, screen_domain);
      if (ok < 0)
      {
        Mceprintf ("Unable to set up navigation for frame %d\n",
                    nav_frame_number);
        Mcdprintf ("M0SetMapNav returns %d\n", ok);
        goto End_Of_Main;
      }
      mapSet = TRUE;
    }


    /*
     * Now we will acquire all of the data requested by the user
     * and put it on a stack. The reason for putting it on the stack
     * is because the server is going to send the data "most recent
     * reports first". But in this command you really want to process
     * "oldest report first" so you don't get watch cancellation
     * messages before you get the watch active message
     */

    /* Loop through and process each item returned by the server */
    while ((ok = M0wtxread(text_header, &text_data)) == 0)
    {

#if 0
/* Here's how to fake a bulletin, very handy for testing and
   a great party trick.  There is also a break statement at
   the end of the segment for-loop) */
text_header[1]=80*23;
text_header[11]=80;
text_header[7] = atoi("WSW");

text_data = (char*)malloc(sizeof(char)*text_header[1]);
(void)memcpy(text_data+(80*0),  "WWUS20 TODD 123456                             
                     2004313 1823", 80);
(void)memcpy(text_data+(80*1),  "WSWTOD                                         
                                 ", 80);
(void)memcpy(text_data+(80*2),  "WIZ001>003-011800-                             
                                 ", 80);
(void)memcpy(text_data+(80*3),  "115 AM CST WED DEC 01 2004                     
                                 ", 80);
(void)memcpy(text_data+(80*4),  
"/z.NEW.TODD.WS.A.1234.041201T0000Z-041201T1800Z/                               
 ", 80);
(void)memcpy(text_data+(80*5),  "WINTER STORM WATCH                             
                                 ", 80);
(void)memcpy(text_data+(80*6),  ";433,0885 433,0894 425,0895 425,0890;          
                                 ", 80);
(void)memcpy(text_data+(80*7),  "$$                                             
                                 ", 80);
(void)memcpy(text_data+(80*8),  "WIZ001>003-012359-                             
                                 ", 80);
(void)memcpy(text_data+(80*9),  
"/z.NEW.TODD.WS.W.0001.041201T0000Z-041201T2359Z/                               
 ", 80);
(void)memcpy(text_data+(80*10), "115 AM CST WED DEC 01 2004                     
                                 ", 80);
(void)memcpy(text_data+(80*11),  "WINTER STORM WARNING                          
                                 ", 80);
(void)memcpy(text_data+(80*12), "$$                                             
                                 ", 80);
(void)memcpy(text_data+(80*13), "WIZ001>003-012359-                             
                                 ", 80);
(void)memcpy(text_data+(80*14), 
"/z.NEW.TODD.SN.Y.0002.041201T0000Z-041201T2359Z/                               
 ", 80);
(void)memcpy(text_data+(80*15), "115 AM CST WED DEC 01 2004                     
                                 ", 80);
(void)memcpy(text_data+(80*16), "SNOW ADIVSORY                                  
                                 ", 80);
(void)memcpy(text_data+(80*17), "$$                                             
                                 ", 80);
(void)memcpy(text_data+(80*18), "WIZ005-011200-                                 
                                 ", 80);
(void)memcpy(text_data+(80*19), 
"/z.CAN.TODD.BL.W.0003.000000T0000Z-041201T1200Z/                               
 ", 80);
(void)memcpy(text_data+(80*20), "115 AM CST WED DEC 01 2004                     
                                 ", 80);
(void)memcpy(text_data+(80*21), "BLIZZARD WARNING                               
                                 ", 80);
(void)memcpy(text_data+(80*22), "$$                                             
                                 ", 80);
#endif

        /* Print full bulletin text to debug output */
        Mcdprintf("\nNew bulletin from server:\n");
        PrintBulletin(text_header, text_data, 0);

        /* Initialize EVENT object member variables */
        InitializeEventObject(&event);

        /* Get segment(s) from message */
        ok = GetTextSegments(text_header, text_data, segment_header,
                             segment_text, &n_segs);
        if ( ok < 0 )
        {
            Mcdprintf("GetTextSegments: memory allocation error\n");
        }

        /* Loop through each segment */
        for (i = 0 ; i < n_segs ; i++)
        {
            int day = 0;
            int time = 0;

            Mcdprintf("\nProcessing segment:\n");
            PrintBulletin(segment_header[i], segment_text[i], 0);

            if ( GetVTECLinesFromText(segment_text[i], segment_header[i],
                                      vtecs, &num_vtecs) == 1 )
            {
                /* This segment has at least one operational VTEC line */
                for (j = 0 ; j < num_vtecs ; j++)
                {
                    /* Decode message and fill the EVENT object */
                    ok = FillEventObject(segment_header[i], segment_text[i],
                         vtecs[j], &event);

                    if (event.product_status[0] == 'T')
                    {
                        Mcdprintf("This is a test message\n");
                        continue;
                    }

                    /* This is a type of event that WWDISP does not display */
                    if ( event.event_type == MAX_EVENTS + 1 )
                    {
                        Mcdprintf("WWDISP is not capable of displaying this 
type of event.\n");
                        continue;
                    }

                    /* Ignore if this event if it is not a type that we're 
interested in */
                    if ( event.event_type == -999 || 
events_array[event.event_type] != TRUE )
                    {
                        Mcdprintf("Event is not of a type that we are 
interested in.\n");
                        continue;
                    }

                    /* If we made it to here then this a valid event that we are
                       interested in so push it onto the stack. */
                    if ( WWSortAndPushOnStack(stack, &event) == 0 )
                    {
                        PrintBulletin(event.header, event.text, 1);
                        Mcdprintf("Pushed new %s (above) onto stack.\n", 
event.phenomenon_long);
                    }
                    else
                    {
                        Mceprintf ("Unable to push data onto stack (memory 
allocation error).\n");
                        goto End_Of_Main;
                    }

                } /* End of VTEC line for-loop */
            }
            else
            {
                /* This segment has no operational VTEC line so we will process
                   it with the old routines. */
                if ( IsTestMessage(segment_header[i], segment_text[i]) )
                {
                    Mcdprintf("This is a test message.\n");
                    continue;
                }

                ok = FillEventObject(segment_header[i], segment_text[i], "\0", 
&event);

                ok = DecodeIssueDayTime(event.text, event.header[11],
                                        event.header[1]/event.header[11],
                                        &day, &time);

                if ( ok == 0)
                {
                    event.beg_date = day;
                    event.beg_time = time;
                }
                else
                {
                    Mcdprintf("DecodeIssueDayTime returned %i\n",ok);
                    Mcdprintf("Failed to decode issue time from text, using 
acquisition time\n");
                    event.beg_date = event.header[10];
                    event.beg_time = event.header[3];
                }

                /* This is a type of event that WWDISP does not display */
                if ( event.event_type == MAX_EVENTS + 1 )
                {
                    Mcdprintf("WWDISP is not capable of displaying this type of 
event.\n");
                    continue;
                }

                /* Ignore if this event if it is not a type that we're 
interested in */
                if ( event.event_type == -999 || events_array[event.event_type] 
!= TRUE )
                {
                    Mcdprintf("Event is not of a type that we are interested 
in.\n");
                    continue;
                }

                /* Get watch number (if this is a watch).  If a VTEC line 
exists then
                   we don't need this because the event tracking number is the 
same as
                   the watch number.  Since there is no VTEC line we have to 
paw through
                   the text to find it. */
                if (!event.isSAW_watchbox)
                /* SAW does not contain "WATCH" in its text */
                {
                  if ( DecodeWatchNumber(event.text, event.header[11],
                                       event.header[1]/event.header[11],
                                       &watch_number) == 0 )
                  {
                    event.event_tracking_number = watch_number;
                  }
                    
                }
                else
                    watch_number=event.event_tracking_number;

                /* If this is a cancelation message then save the event tracking
                   number and the cancellation time in seconds. */
                if ( IsCancellation(event.text, event.header[11],
                                    event.header[1]/event.header[11]) == 1)
                {
                    /*Mcprintf("Doing a cancellation of %i!\n",watch_number);*/
                    
cancel_number[event.event_type][num_cancels[event.event_type]] = watch_number;
                    ok = Mcdaytimetosec(event.beg_date, event.beg_time,
                                        
&cancel_secs[event.event_type][num_cancels[event.event_type]]);
                    num_cancels[event.event_type]++;
                }

                /* If we made it to here then this a valid event that we are 
interested
                   in so push it onto the stack. */
                if ( WWSortAndPushOnStack(stack, &event) == 0 )
                {
                    Mcdprintf("Pushed new %s (above) onto stack.\n", 
event.phenomenon_long);
                }
                else
                {
                    Mceprintf ("Unable to push data onto stack (memory 
allocation error).\n");
                    goto End_Of_Main;
                }
            }

            free(segment_text[i]);

        } /* End of segment for-loop */

#if 0
break; /* for use with a fake bulletin */
#endif

    } /* End of message while-loop */

    /* We're finished reading data from the server (and there was much 
rejoicing) */
    m0cxfin_ ();

  } /* End of selection string for-loop */

  /*
   * Set up frame navigation
   */

  Mcdprintf("\n*** Setting up frame navigation ***\n");

  /* Initialize the graphics for plotting */
  McInitGraphics (output_frame_number, width);
  scloff_ ();

  M0FillInit(output_frame_number,screen_domain[0],screen_domain[2],
             screen_domain[1],screen_domain[3]);

  if (using_image_nav == FALSE)
  {
        (void) McSetGraphicsFrameNumber (output_frame_number);
        (void) McSetImageFrameOn ();
  }

  /* set up graphics paging */
  McSetGraphicsPage (screen_domain[0], screen_domain[2],
                     screen_domain[1], screen_domain[3], 0);

  /* initialize the navigation */
  dum[0] = 0;
  tlat = 0.0;
  tlon = 0.0;
  ok = illtv_ ( &nav_frame_number, &dum[0], &tlat, &tlon,
                &dum[1], &dum[2], &tvline, &tvelem );

  /*
   * Compute the max and min lat/lon of the frame. This is used by the
   * get county lat/lon routines so we don't have to plot any
   * county/zones that aren't navigable on the screen
   */
  ok = GetFrameMinMax (nav_frame_number, &min_lat, &max_lat,
                       &min_lon, &max_lon);

  /*
   * -1 is error. mapdef(), which is called by fmnmx() which is called
   * by GetFrameMinMax(), returns the projection type as a character
   * in a 4-byte integer if ok. We will just check for -1.
   */
  if (ok == -1)
  {
    Mceprintf ("Unable to get lat/lon extents of frame %d\n", ok);
    goto End_Of_Main;
  }

  if (DebugFlag & 0x04)
  {
    Mcprintf ("Frame lat/lon extents=%f %f %f %f\n",
              min_lat, max_lat, min_lon, max_lon);
  }

  Mcdprintf("\n");

  Mcdprintf("\n********************************************************\n");
  Mcdprintf("***       Start popping events from the stack        ***\n");
  Mcdprintf("********************************************************\n\n");


  while ( WWSortAndPopFromStack(stack, &event) )
  {
    int  fill_color;         /* fill color level to use for warnings */

    /* get the color that the event should be plotted in */
    GetPlotColor(event.event_type, color_levs, &plot_color, &fill_color);

    /*Mcprintf("Popping below from the stack!!!\n");
    PrintBulletin(event.header, event.text, 1);*/
    /*
     * decode the raw text data into a list of fips types, fips numbers
     * state ids and expiration times.
     */
     ok = DecodeFIPSFromText(event.text, event.header[11],
                             event.header[1]/event.header[11],
                             event.beg_date, event.beg_time,
                             &n_zones, beg_line, end_line, fips_num,
                             &fips_type, &state, valid_day, valid_time);
      
     if (ok < 0)
     {
       Mcdprintf ("DecodeFIPSFromText returned %i: Unable to decode County/Zone 
information from;\n", ok);
       PrintBulletin(event.header, event.text, 0);
       if (event.isSAW_watchbox && watch_disp_type != WATCH_CTY)
       {
         expired=FALSE;
         /* Check to see if the event is valid for the selected time window */
         ok = EventTimeStatus(&event, beg_day, beg_time, end_day, end_time);
         /*Mcprintf("%i %i %i %i %i %i %i %i\n",event.beg_date, 
event.beg_time,event.end_date,event.end_time,beg_day,beg_time,end_day,end_time);*/
         if ( ok == -1 )
         {
            Mcdprintf("\n**** The %s number %i has expired  ************\n",
                      event.phenomenon_long,event.event_tracking_number);
            PrintBulletin(event.header, event.text, 0);

            /* Check to see if the User wants to plot expired events */
            if ( plot_expired )
              expired = TRUE;
            else
            {
              Mcdprintf("We are not plotting expired events, moving on...\n");
              continue;
            }
         }
         else if ( ok == -2 )
         {
         /* Event begins after the selection window has closed, punt! */
            Mcdprintf("\n**** The %s number %i begins after selection window 
ends ***\n",
                      event.phenomenon_long,event.event_tracking_number);
            PrintBulletin(event.header, event.text, 0);
            continue;
         }

         /* Check to see if the watch has been cancelled before its expiration 
time*/
         for (j=0 ; j < num_cancels[event.event_type] ; j++)
         {
          /*Mcprintf("Cancel number, event tracking number, event_type: %i %i 
%i 
%i\n",cancel_number[event.event_type][j],event.event_tracking_number,event.event_type,j);*/
          if (cancel_number[event.event_type][j] == event.event_tracking_number)
          {
             if (plot_expired)
               expired=TRUE;
             break;
          }
         }

         Mcdprintf("Before decode:%s 
%i\n",event.latlon,event.event_tracking_number);

         ok = DecodeSAW_WatchBox(event.latlon, &n_pairs, lats, lons);
         Mcdprintf("After decode:%s\n",event.latlon);
         /* We are drawing a box */

         PrintLatLons(n_pairs, lats, lons);

         /* set dashing mode according to user specs if event is expired */
         (void) dshoff_();
         if(plot_expired && expired)
         {
              switch(plot_expired)
              {
                case SOLID:
                    break;
                case DASH:
                    (void) dshon_();
                    break;
              }
         }

         PrintBulletin(event.header, event.text, 0);
         
indexWatchList=InWatchList(event.event_tracking_number,watchlist,numwatchlist);
         /* Get center of the watch box for plotting */
         ok = GetWatchBoxCenter(nav_frame_number, lats, lons, n_pairs,
                             &cntr_line, &cntr_elem);

         Mcdprintf("indexWatchList, expired: %i %i\n",indexWatchList,expired);
         /*
          * We want to save off this watch box number and the center location of
          * the box, because we might want to plot or list them later.
         */

         if ( (event.event_type == TSTORM_WATCH || event.event_type == 
TORNADO_WATCH) &&
            indexWatchList >= 0 && EventTimeStatus(&event, beg_day, beg_time, 
end_day, end_time) == 0 &&
            event.event_tracking_number > 0 )
         {
                Mcdprintf("\n**** Plotting %s box for watch number %i 
************\n",
                    event.phenomenon_long, event.event_tracking_number);
                /*for (i=0; i< n_pairs;i++)
                  Mcprintf("To M0DrawLatLonPairs: lats[%i]=%f, 
lons[%i]=%f\n",i,lats[i],i,lons[i]);*/
                ok = M0DrawLatLonPairs(nav_frame_number, lats, lons, n_pairs, 
plot_color,
                                       fill_color, fill_type, ditherFactor);
                Mcdprintf("Adding %i to watch 
tree\n",event.event_tracking_number);
                AddToWatchTree( &watchsave, event.event_tracking_number, 
event.event_type,
                          cntr_line, cntr_elem, event.beg_date, event.beg_time,
                          event.end_date, event.end_time );
                ++n_watch_num_draw;
         }
       }

       continue;
     }

     /*
     * loop through the list of fips numbers that have been decoded for
     * this raw text. If the event has not expired for this county, get
     * the list of lat/lon pairs for the county and plot the results on
     * the graphics frame.
     */

     for (i = 0 ; i < n_zones ; i++)
     {
       int      expires_at;   /* seconds since epoch when the
                                 * event expires */
       int      issued_at;    /* seconds since epoch when the
                                 * event was issued */

       /* If this event is too old or cancelled, bail out of plotting */

       expired = FALSE;

       /* If no VTEC line then use the old methods to get the expiration 
date/time. */
       if ( strlen(event.vtec) == 0 )
       {
         event.end_date = valid_day[i];
         event.end_time = valid_time[i];
       }

       ok = Mcdaytimetosec (event.end_date, event.end_time, &expires_at);
       ok = Mcdaytimetosec (event.beg_date, event.beg_time, &issued_at);

       /* If there was no VTEC line then use the old crusty method for dealing
       with cancellations. */
       if ( strlen(event.vtec) == 0 &&
         (event.event_type == TORNADO_WATCH || event.event_type == 
TSTORM_WATCH) )
       {
         int seconds = -1;
         int day;
         int time;

         for (j=0 ; j < num_cancels[event.event_type] ; j++)
         {
          if (cancel_number[event.event_type][j] == event.event_tracking_number)
          {
             seconds = cancel_secs[event.event_type][j];
             break;
          }
         }

         if ( seconds > 0 )
         {
            ok = Mcsectodaytime(seconds, &day, &time);
            event.end_date = day;
            event.end_time = time;
         }
       }

       /* Check to see if the event is valid for the selected time window */
       ok = EventTimeStatus(&event, beg_day, beg_time, end_day, end_time);
       if ( ok == -1 )
       {
          /* Event has expired */
          if ( fips_type[i][0] == 'C' )
          {
            Mcdprintf("\n**** The %s has expired for %s county %i 
************\n",
                      event.phenomenon_long, state[i], fips_num[i]);
          }
          else if ( fips_type[i][0] == 'Z' )
          {
           Mcdprintf("\n**** The %s has expired for %s zone %i 
**************\n",
                      event.phenomenon_long, state[i], fips_num[i]);
          }
          PrintBulletin(event.header, event.text, 0);

          /* Check to see if the User wants to plot expired events */
          if ( plot_expired )
          {
            expired = TRUE;
          }
          else
          {
            Mcdprintf("We are not plotting expired events, moving on...\n");
            continue;
          }
       }
       else if ( ok == -2 )
       {
         /* Event begins after the selection window has closed, punt! */
         if ( fips_type[i][0] == 'C' )
         {
            Mcdprintf("\n**** The %s begins after selection window ends for %s 
county %i ***\n",
                      event.phenomenon_long, state[i], fips_num[i]);
         }
         else if ( fips_type[i][0] == 'Z' )
         {
            Mcdprintf("\n**** The %s begins after selection window ends for %s 
zone %i ***\n",
                      event.phenomenon_long, state[i], fips_num[i]);
         }
         PrintBulletin(event.header, event.text, 0);
         continue;
        }

       /* If we've made it to here then we have at least one event to plot */
       at_least_one = TRUE;

       /* Decode the lat/lon pairs for the box. */
       if ( strlen(event.vtec) == 0)
       {
          ok = DecodeWatchBoxFromText(event.text, event.header[11],
                                    event.header[1]/event.header[11],
                                    &n_pairs, lats, lons);
       }
       else
       {
        ok = DecodeWatchBox(event.latlon, &n_pairs, lats, lons);
       }
         /* If ok != 0, this is not a valid lat/lon message and therefore 
cannot be plotted
         if (ok < 0)
           continue;*/

       /* Get and save the center of the watch box for possible use later. */
       ok = GetWatchBoxCenter(nav_frame_number, lats, lons, n_pairs,
                             &cntr_line, &cntr_elem);

      if (ok >= 0)
      {
         ok = McSetDashAttributes(7, 5, 255);
         fill_type = OPEN;
      }

      /*
       * We want to save off this watch box number and the center location of
       * the box, because we might want to plot or list them later.
      */

      if ( (event.event_type == TSTORM_WATCH || event.event_type == 
TORNADO_WATCH) &&
            InWatchList(event.event_tracking_number, watchlist, numwatchlist) 
>= 0 &&
            EventTimeStatus(&event, beg_day, beg_time, end_day, end_time) == 0 
&&
            event.event_tracking_number != -999 )
      {
          AddToWatchTree( &watchsave, event.event_tracking_number, 
event.event_type,
                          cntr_line, cntr_elem, event.beg_date, event.beg_time,
                          event.end_date, event.end_time );
          ++n_watch_num_draw;
      }

      /* These products are always county/zone based */
      if (event.event_type == TORNADO_WARN  ||
          event.event_type == TSTORM_WARN   ||
          event.event_type == FLOOD_WARN    ||
          event.event_type == FLOOD_WATCH   ||
          event.event_type == BLIZZARD_WARN ||
          event.event_type == WSTORM_WARN   ||
          event.event_type == WSTORM_WATCH  ||
          event.event_type == SNOW_ADVISORY ||
          watch_disp_type  == WATCH_CTY)
      {
          fill_type = warning_fill_type;
          ok = McSetDashAttributes(4, 3, 255);
      }

      /* If we are drawing a county or zone */
      if(watch_disp_type  == WATCH_CTY      ||
         event.event_type == TORNADO_WARN   ||
         event.event_type == TSTORM_WARN    ||
         event.event_type == FLOOD_WATCH    ||
         event.event_type == FLOOD_WARN     ||
         event.event_type == BLIZZARD_WARN  ||
         event.event_type == WSTORM_WARN    || 
         event.event_type == WSTORM_WATCH   ||
         event.event_type == SNOW_ADVISORY)
      {
          int   state_fips_num;
          char  *state_name;
          int   segment;
          int   old_fill_type;

          /* Initialize lat/lon subsystem */
          ok = McInitGetLL();

          /* Get state fips number from state PO */
          ok = McGetStateFipsFromPO(state[i], &state_fips_num, &state_name);
          if (ok != 0)
          {
            Mcdprintf ("Unable to get state FIPS number for state %s\n", 
state[i]);
            continue;
          }

          /*
           * Save this event in a structure so we won't plot this county/zone
           * more than once
           */
          if (watch_disp_type == WATCH_CTY)
          {
             if (InWatchList(event.event_tracking_number, watchlist, 
numwatchlist)>=0)
             {
               if ( EventPlotted(event.event_type, state_fips_num, fips_num[i], 
fips_type[i]) == 1 )
               {
                 if (fips_type[i][0] == 'C')
                   Mcdprintf("Event for %s county %i has already been 
plotted.\n",state[i],fips_num[i]);
                 else
                   Mcdprintf("Event for %s zone %i has already been 
plotted.\n",state[i],fips_num[i]);

                 PrintBulletin(event.header, event.text, 0);
                 continue;
               }
             }
          }
          else
          {
            if ( EventPlotted(event.event_type, state_fips_num, fips_num[i], 
fips_type[i]) == 1 )
            {
              if (fips_type[i][0] == 'C')
                Mcdprintf("Event for %s county %i has already been 
plotted.\n",state[i],fips_num[i]);
              else
                Mcdprintf("Event for %s zone %i has already been 
plotted.\n",state[i],fips_num[i]);

              PrintBulletin(event.header, event.text, 0);
              continue;
            }
          }

          /* get the lat/lon for this county/zone and plot it */

          ok = McFindLLFromStateNum (1, &fips_type[i], &state_fips_num,
                                     &fips_num[i], min_lat, max_lat,
                                     min_lon, max_lon);

          if ( ok == 0 )
          {
            /* The region wasn't on the screen. Get the next region. */
            Mcdprintf("Region not on screen.\n");
            continue;
          }
          else if ( ok < 0 )
          {
            /* This county/zone couldn't be found */
            if (fips_type[i][0] == 'C')
            {
                Mcdprintf("Unable to find county number %d for the state of 
%s\n",
                          fips_num[i], state[i]);
            }
            else
            {
                Mcdprintf("Unable to find zone number %d for the state of %s\n",
                          fips_num[i], state[i]);
            }

            continue;
          }

          /* Print message */
          if ( fips_type[i][0] == 'C' )
          {
                Mcdprintf("\n**** Plotting %s for %s county %i watch_number %i 
************\n",
                          event.phenomenon_long, state[i], 
fips_num[i],event.event_tracking_number);
          }
          else if ( fips_type[i][0] == 'Z' )
          {
                Mcdprintf("\n**** Plotting %s for %s zone %i **************\n",
                          event.phenomenon_long, state[i], fips_num[i]);
          }

          /* Set snow advisories and flood warnings to hatch */
          old_fill_type = fill_type;

          if (event.event_type == SNOW_ADVISORY && fill_type != OPEN)
          {
            fill_type = HATCH;
            fill_color = color_levs[6];
          }

          if ((event.event_type == FLOOD_WARN || event.event_type == 
FLOOD_WATCH) &&
               fill_type != OPEN) 
          { 
            fill_type = HATCH;
          }

          /*
           * Continue to retrieve and plot segments for this region until there
           * are no more segments
           */
          segment = 0;
          while(1)
          {
            n_pairs = McGetLineSegs (lats, lons, &segment);
            if (n_pairs == 0) break;
            if (n_pairs < 0)
            {
                Mcdprintf ("Failed to retrieve line segment %d for %s%d\n",
                           segment, state[i], fips_num[i]);
                break;
            }

            PrintLatLons(n_pairs, lats, lons);

            /* set dashing mode according to user specs if event is expired */
            (void) dshoff_();
            if(plot_expired && expired)
            {
                switch(plot_expired)
                {
                  case SOLID:
                      break;
                  case DASH:
                      (void) dshon_();
                      break;
                }
            }

            if (watch_disp_type == WATCH_CTY)
            {
              if (InWatchList(event.event_tracking_number, watchlist, 
numwatchlist)>=0)
                 ok = M0DrawLatLonPairs (nav_frame_number, lats, lons, n_pairs, 
plot_color,
                                    fill_color, fill_type, ditherFactor);
            }
            else
              ok = M0DrawLatLonPairs (nav_frame_number, lats, lons, n_pairs, 
plot_color,
                                    fill_color, fill_type, ditherFactor);

          }

          fill_type = old_fill_type;
      }
      else
      {
          /* We are drawing a box */

          PrintLatLons(n_pairs, lats, lons);

          /* set dashing mode according to user specs if event is expired */
          (void) dshoff_();
          if(plot_expired && expired)
          {
              switch(plot_expired)
              {
                case SOLID:
                    break;
                case DASH:
                    (void) dshon_();
                    break;
              }
          }

          if (event.event_tracking_number > 0 && strlen(event.latlon)>0)
             Mcdprintf("\n**** Plotting %s box for watch number %i 
************\n",
                    event.phenomenon_long, event.event_tracking_number);
          PrintBulletin(event.header, event.text, 0);

          
indexWatchList=InWatchList(event.event_tracking_number,watchlist,numwatchlist);
          if ( indexWatchList >= 0 && strlen(event.latlon) > 0 && 
event.event_tracking_number > 0) 
          {
               /*for (i=0; i< n_pairs;i++)
                  Mcprintf("To M0DrawLatLonPairs: lats[%i]=%f, 
lons[%i]=%f\n",i,lats[i],i,lons[i]);*/
               ok = M0DrawLatLonPairs(nav_frame_number, lats, lons, n_pairs, 
plot_color,
                                       fill_color, fill_type, ditherFactor);
          }
      }
    } 

    /* bind off the graphics and free any memory no longer needed */

    if (at_least_one == TRUE) enpt_ ();

    VecOld (fips_type);
    VecOld (state);

  } /* end of while */

  if (at_least_one == TRUE)
  {
    endplt_ ();

    /* initialize the graphics package for plotting title and legend */

    McInitGraphics (output_frame_number, 0);
    scloff_ ();

    if (using_image_nav == FALSE)
    {
      (void) McSetGraphicsFrameNumber (output_frame_number);
      (void) McSetImageFrameOn ();
    }

    /* write out the titles */
    M0FillInit(output_frame_number,0,0,99999,99999);

    DrawTitles (beg_day, beg_time, end_day, end_time, screen_domain[0],
                screen_domain[2], screen_domain[1],
                screen_domain[3], events_array, color_levs, 
                warning_fill_type, ditherFactor);

    enpt_ ();

    /* all graphics have been plotted, draw the map */
    ok = M0DrawMap (output_frame_number, mapbase, projection, screen_domain);
    if (ok < 0)
    {
      Mceprintf ("Unable to draw %s\n", mapbase);
      goto End_Of_Main;
    }
  }
  else
  {
    Mcprintf("No active watches or warnings found given selection 
conditions\n");
  }

  /*
   * if we have a watch number to draw, then draw the watch number in
   * the center of the box.  n_watch_num_draw will be 0 if watches are
   * plotted as county outlines or PLOT=BOX was specified
   */ 
  if (at_least_one && n_watch_num_draw > 0)
  {
    /* initialize the graphics package for plotting watch numbers */

    McInitGraphics (output_frame_number, 0);
    scloff_ ();

    if (using_image_nav == FALSE)
    {
      (void) McSetGraphicsFrameNumber (output_frame_number);
      (void) McSetImageFrameOn ();
    }

    /* set up graphics paging */

    McSetGraphicsPage (screen_domain[0],
                       screen_domain[2],
                       screen_domain[1],
                       screen_domain[3],
                       0);

    /*
     * traverse the watch number tree and call DrawWatchBoxNumber to draw
     * the watch box number in the center of the box
     */
    if (plot_watch_number)
        TraverseWatchTree(watchsave, DrawWatchBoxNumber, color_levs,
                          &watch_num_draw_height);

    /*
     * traverse the watch number tree and call DrawWatchBoxList to draw
     * the watch box number list in the upper left corner of screen
     */
    if (plot_watch_list)
        TraverseWatchTree(watchsave, DrawWatchBoxList, screen_domain,
                          color_levs);

    endplt_();

  }

  /* set original dash settings - need to return upon completion */
  ok = McSetDashAttributes(orig_dash_len, orig_gap_len, orig_gap_color);
  if (ok < 0) { goto End_Of_Main; }

  /* assign successful completion */

  return_val = 0;

  /* exit out of the program */

  End_Of_Main:
  M0FillClose();
  
  Mccodeset (return_val);
  Mceprintf ("done\n");
  return (return_val);
}


/*
** Name:
**    GetPlotColor - get the plotting color based on event type
**
** Interface:
**    static void
**    GetPlotColor (int event_type, int *color_levs, int *color,
**                  int *fill_color)
**
** Input:
**    event_type    - event type to find color for
**    color_levs    - list of color levels to chose from
**
** Input and Output:
**    none
**
** Output:
**    color         - output color level
**    fill_color    - fill color level if any
**
** Return values:
**    none
**
*/

static void
  GetPlotColor (
    int         event_type,
    int         *color_levs,
    int         *color,
    int         *fill_color)
{

  *fill_color = 0;

  switch (event_type)
  {
    case TSTORM_WATCH:
      *color       = color_levs[1];
      break;

    case TSTORM_WARN:
      *color       = color_levs[3];
      *fill_color  = color_levs[1];
      break;

    case TORNADO_WATCH:
      *color       = color_levs[0];
      break;

    case TORNADO_WARN:
      *color       = color_levs[2];
      *fill_color  = color_levs[0];
      break;

    case FLOOD_WATCH:
      *color       = color_levs[10];
      *fill_color  = color_levs[10];
      break;

    case FLOOD_WARN:
      *color       = color_levs[9];
      *fill_color  = color_levs[9];
      break;

    case WSTORM_WATCH:
      *color       = color_levs[4];
      break;

    case WSTORM_WARN:
      *color       = color_levs[5];
      *fill_color  = color_levs[4];
      break;

    case SNOW_ADVISORY:
      *color       = color_levs[6];
      break;

    case BLIZZARD_WARN:
      *color       = color_levs[8];
      *fill_color  = color_levs[7];
      break;
  }
  return;
}

/*
** Name:
**    M0DrawLatLonPairs - draw a list of lat/lon pairs to a graphic frame
**
** Interface:
**    static int
**    M0DrawLatLonPairs (int frame, double *lats, double *lons, int n_pairs,
**                       int color, int fillcolor, int filltype)
**
** Input:
**    frame         - frame to use for navigation
**    lats          - latitude list
**    lons          - longitude list
**    n_pairs       - number of pairs to plot
**    color         - color level to plot
**    fillcolor     - color level to fill
**    filltype      - what type of filling to use
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    0             - success
**   -1             - too many pairs to plot
**   -2             - no portion of the lines are on the screen
*/

static int
  M0DrawLatLonPairs (
    int            nav_frame_number,
    double        *lats,
    double        *lons,
    int            n_pairs,
    int            color,
    int            fillcolor,
    int            filltype,
    int            ditherFactor)
{
  static int      tvlines[MAX_LATLON];      /* list of tv lines */
  static int      tvelems[MAX_LATLON];      /* list of tv elements */
  static int      onscreen[MAX_LATLON];     /* flag indicating what is on
                                             * the screen */
  int             i;                        /* loop counter */
  int             j;                        /* loop counter */
  int             dash_mode;                /* saved dashing mode */
  int             dum[3];                   /* dummy variables for illtv call */
  int             n_polygon;                /* number of polygons to draw */
  int            *first_point = NULL;       /* array containing list
                                             * of first point locations
                                             * for each polygon */
  int            *n_per_poly = NULL;        /* number of pairs per polygon */
  Freal           tlat;                     /* lat value for illtv() */
  Freal           tlon;                     /* lon value for illtv() */

  /* too many pairs */

  if (n_pairs > MAX_LATLON) { return (-1); }

  n_polygon = NumPolygon (n_pairs, lats, lons, &first_point, &n_per_poly);

  if (DebugFlag & 0x08)
  {
      Mcprintf ("NumPolygon returns %d polygons\n", n_polygon);
  }

  for (j = 0 ; j < n_polygon ; j++)
  {
    int             n_valid_points = 0;       /* number of valid points */
    int             first_good_point = -1;    /* flag indicating where
                                               * first good point is */

    memset (tvlines, 0, sizeof (int) * n_per_poly[j]);
    memset (tvelems, 0, sizeof (int) * n_per_poly[j]);
    memset (onscreen, 0, sizeof (int) * n_per_poly[j]);

    if (DebugFlag & 0x08)
    {
      Mcprintf ("Trying to draw %d pairs\n", n_per_poly[j]);
    }

    /* loop through the list of lat/lons and convert to tvlines/eles */

    for (i = 0 ; i < n_per_poly[j] ; i++)
    {
      tlat = (Freal) lats[i + first_point[j]];
      tlon = (Freal) lons[i + first_point[j]];

      dum[0] = 0;

      /* convert the lat/lon to tvlines/eles */

      onscreen[i] = illtv_ (&nav_frame_number, &dum[0], &tlat, &tlon,
                            &dum[1], &dum[2], &tvlines[i], &tvelems[i]);

      if (DebugFlag & 0x08)
      {
        Mcprintf ("illtv=%2d %i %7.2f %7.2f %d %d\n",
                    onscreen[i], nav_frame_number, tlat,
                    tlon, tvlines[i], tvelems[i]);
      }

      /* if the point is on the screen */

      if (onscreen[i] == 0)
      {
        n_valid_points++;
        if (first_good_point == -1) { first_good_point = i; }
      }
    }

    /* points were found on the screen */

    if (n_valid_points > 0)
    {
      /* if this county is to be filled */

      /* dashing mode must be off to do filling, so save the current
       * status of dashing mode and turn it off.
      */
      (void) qgdash_((Fint *)&dash_mode);
      if (dash_mode != 0) (void) dshoff_();

      if (filltype != 0 && fillcolor)
      {
        switch(filltype)
        {
          case SOLID:
            M0FastFill(n_per_poly[j], tvlines, tvelems, fillcolor 
,FAST_FILL_SOLID,0);
            break;
          case DITHER:
            M0FastFill(n_per_poly[j], tvlines, tvelems, fillcolor 
,FAST_FILL_DITHER, ditherFactor);
            break;
          case HATCH:
            M0FastFill(n_per_poly[j], tvlines, tvelems, fillcolor 
,FAST_FILL_HATCH, 5);
            break;
        }
      }

      /* restore dash mode */
      if (dash_mode != 0) (void) dshon_();

      /* draw the outline */
      McDrawLineSegments (n_per_poly[j] , tvlines, tvelems, color);
    }
  }

  free (first_point);
  free (n_per_poly);

  return (0);
}

/*
*| Name:
*|    M0SetMapNav - sets the frame navigation for the appropriate
*|                  map
*|
*| Interface:
*|    static int
*|    M0SetMapNav (int nav_frame, int frame, const char *map,
*|                 const char *projection, int *screen_domain)
*|
*| Input:
*|    nav_frame     - frame to use to get navigation
*|    frame         - frame number to initialize
*|    map           - map name to use for navigation
*|    projection    - projection type
*|    screen_domain - min and max line and element for the screen
*|
*| Input and Output:
*|    none
*|
*| Output:
*|    none
*|
*| Return values:
*|    0                 - success
*|   <0                 - map definition error
*/
static int
  M0SetMapNav (
    int        nav_frame,
    int        frame,
    const char *map,
    const char *projection,
    int        *screen_domain)
{
  float        mapdeflatlon[4];                   /* map lat/lon definitions */
  float        mapparms[4]={0.0,0.0,0.0,0.0};     /* definitions for conf
                                                   * projections */
  int          ok;                                /* function return value */
  int          screen[4];                         /* screen dimensions */
  FsLen        proj_len;                          /* length of projection
                                                   * string */
  char        *frame_proj = NULL;                 /* frame projection */

  memcpy (screen, screen_domain, sizeof (screen));

  /* if sat is specified */

  if (strcmp (map, "SAT") == 0)
  {
    int          i;                     /* loop counter */
    char        *frame_nav_type;        /* frame navigation type */
    Fint         nav_block[1024];       /* frame navigation block */


    /* if frame navigation is specified and there isn't any, exit */

    ok = (int) frtonv_ ((Fint *) &nav_frame, nav_block);
    if (ok != 0) { return (-10); }

    frame_nav_type = fsalloc ((char *) &nav_block[0], 4);
    frame_proj     = fsalloc ((char *) &nav_block[20], 4);

    if (strcmp (frame_nav_type, "GRAF") == 0)
    {
      for (i = 0; i < 4; i++)
      {
        mapdeflatlon[i] = (float) nav_block[i+21] / 10000.0;
      }
      for (i = 0 ; i < 3; i++)
      {
        mapparms[i] = (float) nav_block[i+29] / 10000.0;
      }
      mapparms[3] = (float) nav_block[32] * 1.0;
      memcpy (screen, &nav_block[25], sizeof (screen));
    }
    else
    {
      free (frame_nav_type);
      return (0);
    }

    free (frame_nav_type);
  }

  /* if mercator is specified */

  if (strcmp (projection, "MERC") == 0)
  {
    float        north;
    float        south;
    float        east;
    float        west;

    ok = McGetMapMercatorNav (map, &south, &north, &east, &west);
    if (ok < 0) { return (ok); }

    mapdeflatlon[0] = south;
    mapdeflatlon[1] = north;
    mapdeflatlon[2] = east;
    mapdeflatlon[3] = west;
    frame_proj      = strdup ("MERC");
  }

  /* if a conformal projection is specified */

  if (strcmp (projection, "CONF") == 0)
  {
    float           slat1;
    float           slat2;
    float           slon;
    float           scale;

    ok = McGetMapPolarStereoNav (map, mapdeflatlon, &slat1, &slat2,
                                 &slon, &scale);
    if (ok < 0) { return (ok); }

    mapparms[0] = slat1;
    mapparms[1] = slat2;
    mapparms[2] = slon;
    mapparms[3] = scale;
    frame_proj  = strdup ("CONF");

    Mcdprintf ("                       s1=%7.2f s2=%7.2f sl=%7.2f sc=%f\n",
               slat1, slat2, slon, scale);
  }

  Mcdprintf ("maplatlons= %7.2f %7.2f %7.2f %7.2f\n", mapdeflatlon[0],
             mapdeflatlon[1], mapdeflatlon[2], mapdeflatlon[3]);
  Mcdprintf ("Screen domain %4d %4d lines  %4d %4d elements '%s'\n",
             screen[0], screen[1], screen[2], screen[3], frame_proj);

  /* assign the navigation */

  proj_len = strlen (frame_proj);
  if (proj_len != 0)
  {
    mapsav_ (&frame, mapdeflatlon, screen, frame_proj, mapparms, proj_len);
  }

  free (frame_proj);

  return (0);
}

/*
** Name:
**    PrintLatLons - print a list of lat/lons
**
** Interface:
**    static void
**    PrintLatLons (int n_pairs, double *lats, double *lons)
**
** Input:
**    n_pairs       - number of lat/lon pairs
**    lats          - array of lats to print
**    lons          - array of lons to print
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    none
*/

static void
  PrintLatLons (
    int      n_pairs,
    double   *lats,
    double   *lons)
{
  int i;

  if (DebugFlag & 0x02)
  {
    for (i = 0 ; i < n_pairs; i++)
    {
      Mcprintf ("%5d   %7.2f  %8.2f\n", i, lats[i], lons[i]);
    }
  }

  return;
}

/*
** Name:
**    M0DrawMap - function call jacket for McIDAS MAP command
**
** Interface:
**    static int
**    M0DrawMap (int frame, const char *mapname, double *latlon,
**               const char *projection, screen_domain)
**
** Input:
**    frame         - graphic frame to write to
**    mapname       - name of map to draw
**    projection    - map projection to draw in
**    screen_domain - line min/max and element min/max to contain map
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    0             - success
**   -1             - no map found
**   -2             - MAP command failed
**
** Remarks:
**    This function is a no op for SAT.
*/

static int
  M0DrawMap (
    int              frame,
    const char      *mapname,
    const char      *projection,
    int             *screen_domain)
{
  int          ok;                       /* function return value */
  char         command[1000];            /* actual map command to run */
  char         t_string[100];

  /* if the SAT projection is specified, don't do anything */

  if (strcmp (mapname, "SAT") == 0)
  {
    return (0);
  }

  /*
   * build the MAP command name, we just keep concatonating options on
   * the command string until we have everything
   */

  strcpy  (command, "MAP SAT DEV=NNN ");

  strcat (command, "BOX=YES ");
  strcat (command, "ST=ALL ");

  sprintf (t_string, "GRA=%d ", frame);
  strcat  (command, t_string);

  sprintf (t_string, "IMA=%d ", frame);
  strcat  (command, t_string);

  /* run the command */

  ok = Mcskeyin (command);
  if (ok != 0) { return (-2); }

  return (0);
}

/*
** Name:
**    GetColorLevels - get the color levels used from the command line
**
** Interface:
**    static int
**    GetColorLevels (int *color_levs, int max_color)
**
** Input:
**    max_color     - maximum color level allowed for this workstation
**
** Input and Output:
**    none
**
** Output:
**    color_levs    - array of 4 color levels for thunderstorm, tornado,
**                    thuderstorm county outline and tornado county
**                    outline.
**
** Return values:
**    0             - success
**   <0             - command line entry error
*/
static int
  GetColorLevels (
    int *color_levs,
    int  max_color)
{
  int              *colors;       /* colors array */
  int              ok;            /* function return value */
  int              i;             /* loop counter */
  int              deflt[MAX_EVENTS]       = { 2,3,5,4,14,10,11,7,8,6,9 };
  int              short_deflt[MAX_EVENTS] = { 2,3,5,4, 6, 2, 4,7,8,6,1 };

  /*
   * based on how many color levels are available, determine which
   * default colors we will use.
   */
  if (max_color < 9) 
  {
      Mcdprintf("Using short list of color levels\n");
      colors = short_deflt;
  }
  else
  {
      colors = deflt;
  }

  for (i = 0 ; i < MAX_EVENTS ; i++)
  {
    ok = Mccmdint ("COL.OR", (i + 1), "Color Level", colors[i],
                   1, max_color, &color_levs[i]);
    if (ok < 0) return (ok);
  }

  return (0);
}

/*
** Name:
**    GetFillType - get the scheme of filling warning boxes
**
** Interface:
**    static int
**    GetFillType (int *fillType)
**
** Input:
**    none
**
** Input and Output:
**    none
**
** Output:
**    fillType      - Method for filling
**                      0 - open
**                      1 - solid
**                      2 - dithered
**
** Return values:
**    0             - success
**   <0             - command line entry error
*/
static int
  GetFillType (
    int *fillType)
{
  int              ok;            /* function return value */
  int              i;             /* loop counter */
  char             *userfill;
  char             *options[] = {
                        "NONE",
                        "SOLID",
                        "TRAN"
                        };

  int              values[NUMFILLTYPES] = {   /* default color levels */
                        OPEN,
                        SOLID,
                        DITHER};

  /* default fill is SOLID */

  *fillType = SOLID;

  ok = Mccmdstr("FIL.L", 1, "SOLID", (const char **)&userfill);
  if (ok < 0) { return ok; }

  for (i = 0 ; i < NUMFILLTYPES ; i++)
  {
    if (userfill[0] == options[i][0]) { *fillType = values[i]; }
  }

  return (0);
}

/*
** Name:
**    DrawTitles - draw the titles for the graphics
**
** Interface:
**    static void
**    DrawTitles (int beg_day, int beg_time, int end_day,
**                int end_time, int minlin, int minele,
**                int maxlin, int maxele, int events_array,
**                int *color_levs,int filltype, int ditherFactor)
**
** Input:
**    beg_day    - beginning day of time window
**    beg_time   - beginning time of time window
**    end_day    - ending day of time window
**    end_time   - ending time of time window
**    minlin     - top line number of graphic
**    minele     - leftmost element
**    maxlin     - bottom line number of graphic
**    maxele     - rightmost element
**    events_array - flags for each product type
**    color_levs - list of colorlevels
**    filltype   - fill polygon type
**    ditherFactor - ditherFactor if filltype=DITHER
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    none
*/
static void
  DrawTitles (
    int  beg_day,
    int  beg_time,
    int  end_day,
    int  end_time,
    int  minlin,
    int  minele,
    int  maxlin,
    int  maxele,
    int *events_array,
    int *color_levs,
    int filltype,
    int ditherFactor)
{
  int     height = 7;        /* height of titles */
  int     lin[5];            /* line coordinates of a box */
  int     ele[5];            /* element coordinates of a box */
  int     ok;                /* function return value */
  int     text_ele;          /* element coordinate to place text */
  char   *beg_day_string;        /* day string storage */
  char   *end_day_string;        /* day string storage */
  char   *beg_time_string;       /* time string storage */
  char   *end_time_string;       /* time string storage */
  char   *t_string;          /* temporary string */

  /* print the title at the top */

  beg_time = beg_time / 100 * 100;
  end_time = end_time / 100 * 100;
  ok = Mccydtostr (beg_day, 6, &beg_day_string);
  ok = Mccydtostr (end_day, 6, &end_day_string);
  ok = Mchmstostr (beg_time, 5, &beg_time_string);
  ok = Mchmstostr (end_time, 5, &end_time_string);

  beg_time_string[5] = '\0';
  end_time_string[5] = '\0';

  if (beg_day == end_day && beg_time == end_time)
  {
      t_string = stralloc ("Active severe weather bulletins as of ",
                           end_day_string, " ", end_time_string, (char *) NULL);
  }
  else if (beg_day == end_day && beg_time != end_time)
  {
      t_string = stralloc ("Active severe weather bulletins for period ",
                           end_day_string, " ", beg_time_string, " - ",
                           end_time_string, (char *) NULL);
  }
  else
  {
      t_string = stralloc ("Active severe weather bulletins for period ",
                           beg_day_string, " ", beg_time_string, " - ",
                           end_day_string, " ", end_time_string, (char *) NULL);
  }

  (void) McDrawText (t_string, 0, 5, height, minlin - 3,
                     ((maxele-minele)/2) + minele, "C", "H", 1, 0);

  free (beg_day_string);
  free (end_day_string);
  free (beg_time_string);
  free (end_time_string);
  free (t_string);

  /* print the legend at the bottom */

  (void) McDrawText ("Watch", 0, 5, height, maxlin + 15,
                     minele + 10, "W", "H", 0, 0);

  (void) McDrawText ("Warning", 0, 5, height, maxlin + 30,
                     minele + 10, "W", "H", 0, 0);

  lin[0]  = maxlin + 17;
  lin[1]  = lin[0];
  lin[2]  = lin[0] + 8;
  lin[3]  = lin[2];
  lin[4]  = lin[0];

  text_ele = minele + 80;

  /* draw box for severe thunderstorm watches and warnings */

  if (events_array[TSTORM_WATCH] || events_array[TSTORM_WARN])
  {
      (void) McDrawText ("T-Storm", 0, color_levs[1], height,
                         maxlin + 5, text_ele, "L", "H", 0, 0);

      ele[0]  = text_ele + 15;
      ele[1]  = ele[0] + 20;
      ele[2]  = ele[1];
      ele[3]  = ele[0];
      ele[4]  = ele[0];

      if (events_array[TSTORM_WATCH])
      {
          McDrawLineSegments (5, lin, ele, color_levs[1]);
      }

      /* draw filled in box for severe thunderstorm warning */

      lin[0]  = lin[0] + 14;
      lin[1]  = lin[0];
      lin[2]  = lin[0] + 8;
      lin[3]  = lin[2];
      lin[4]  = lin[0];

      if (events_array[TSTORM_WARN])
      {
          switch (filltype)
          {
            case SOLID:
              M0FastFill(5, lin, ele, color_levs[1],FAST_FILL_SOLID,0);
              break;
            case OPEN:
              break;
            case DITHER:
              M0FastFill(5, lin, ele, 
color_levs[1],FAST_FILL_DITHER,ditherFactor);
              break;
          }

          McDrawLineSegments (5, lin, ele, color_levs[3]);
      }

      text_ele += 75;
  }

  /* draw tornado watch and warning boxes */
  if (events_array[TORNADO_WATCH] || events_array[TORNADO_WARN])
  {
      (void) McDrawText ("Tornado", 0, color_levs[0], height,
                         maxlin + 5, text_ele, "L", "H", 0, 0);

      /* draw box for tornado watch */

      lin[0] = maxlin + 17;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      ele[0] = text_ele + 15;
      ele[1] = ele[0] + 20;
      ele[2] = ele[1];
      ele[3] = ele[0];
      ele[4] = ele[0];

      if (events_array[TORNADO_WATCH])
      {
          McDrawLineSegments (5, lin, ele, color_levs[0]);
      }

      /* draw filled in box for tornado warning */

      lin[0] = lin[0] + 14;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      if (events_array[TORNADO_WARN])
      {
          switch (filltype)
          {
            case SOLID:
              M0FastFill(5, lin, ele, color_levs[0],FAST_FILL_SOLID,0);
              break;
            case OPEN:
              break;
            case DITHER:
              M0FastFill(5, lin, ele, 
color_levs[0],FAST_FILL_DITHER,ditherFactor);
              break;
          }

          McDrawLineSegments (5, lin, ele, color_levs[2]);
      }

      text_ele += 70;
  }

  /* draw boxes for winter storm watch and warning */

  if (events_array[WSTORM_WATCH]  || events_array[WSTORM_WARN])
  {
      (void) McDrawText ("Winter Storm", 0, color_levs[4], height,
                         maxlin + 5, text_ele, "L", "H", 0, 0);

      lin[0] = maxlin + 17;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      ele[0] = text_ele + 35;
      ele[1] = ele[0] + 20;
      ele[2] = ele[1];
      ele[3] = ele[0];
      ele[4] = ele[0];

      if (events_array[WSTORM_WATCH])
      {
          McDrawLineSegments (5, lin, ele, color_levs[4]);
      }

      /* draw box for winter storm warning */

      lin[0] = lin[0] + 14;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      if (events_array[WSTORM_WARN])
      {
          switch (filltype)
          {
            case SOLID:
              M0FastFill(5, lin, ele, color_levs[4],FAST_FILL_SOLID,0);
              break;
            case OPEN:
              break;
            case DITHER:
              M0FastFill(5, lin, ele, 
color_levs[4],FAST_FILL_DITHER,ditherFactor);
              break;
          }

          McDrawLineSegments (5, lin, ele, color_levs[5]);
      }

      text_ele += 100;
  }

  /* draw boxes for blizzard watches and warnings */

  if (events_array[BLIZZARD_WARN])
  {
      (void) McDrawText ("Blizzard", 0, color_levs[7], height,
                         maxlin + 5, text_ele, "L", "H", 0, 0);

      lin[0] = maxlin + 31;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      ele[0] = text_ele + 17;
      ele[1] = ele[0] + 20;
      ele[2] = ele[1];
      ele[3] = ele[0];
      ele[4] = ele[0];

      if (events_array[BLIZZARD_WARN])
      {
          switch (filltype) 
          {
            case SOLID:
              M0FastFill(5, lin, ele, color_levs[7],FAST_FILL_SOLID,0);
              break;
            case OPEN:
              break;
            case DITHER:
              M0FastFill(5, lin, ele, 
color_levs[7],FAST_FILL_DITHER,ditherFactor);
              break;
          }

          McDrawLineSegments (5, lin, ele, color_levs[8]);
      }

      text_ele += 75;
  }

  /* draw box for snow advisories */

  if (events_array[SNOW_ADVISORY])
  {
      (void) McDrawText ("Snow Advisory", 0, color_levs[6], height,
                         maxlin + 5, text_ele, "L", "H", 0, 0);

      lin[0] = maxlin + 31;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      ele[0] = text_ele + 40;
      ele[1] = ele[0] + 20;
      ele[2] = ele[1];
      ele[3] = ele[0];
      ele[4] = ele[0];

      switch (filltype)
      {
        case OPEN:
          break;
        default:
          M0FastFill(5, lin, ele, color_levs[6],FAST_FILL_HATCH,5);
          break;
      }

      McDrawLineSegments (5, lin, ele, color_levs[6]);

      text_ele += 110;
  }

  /* draw flash flood watch and warning boxes */

  if (events_array[FLOOD_WATCH] || events_array[FLOOD_WARN])
  {
      (void) McDrawText ("Flood", 0, color_levs[10], height,
                         maxlin + 5, text_ele, "L", "H", 0, 0);

      lin[0] = maxlin + 17;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      ele[0] = text_ele + 7;
      ele[1] = ele[0] + 20;
      ele[2] = ele[1];
      ele[3] = ele[0];
      ele[4] = ele[0];

      if (events_array[FLOOD_WATCH])
      {
          switch (filltype) 
          {
            case OPEN:
              break;
            default:
              M0FastFill(5, lin, ele, color_levs[10],FAST_FILL_HATCH,5);
              break;
          }

          McDrawLineSegments (5, lin, ele, color_levs[10]);
      }

      /* draw box for flood warning */

      lin[0] = lin[0] + 14;
      lin[1] = lin[0];
      lin[2] = lin[0] + 8;
      lin[3] = lin[2];
      lin[4] = lin[0];

      if (events_array[FLOOD_WARN])
      {
          switch (filltype) 
          {
            case OPEN:
              break;
            default:
              M0FastFill(5, lin, ele, color_levs[9],FAST_FILL_HATCH,5);
              break;
          }
          
          McDrawLineSegments (5, lin, ele, color_levs[9]);
      }

      text_ele += 100;
  }

  return;
}


/*
** Name:
**    NumPolygon - subsects a polygon outline into sub-polygons
**
** Interface:
**    static int NumPolygon (int n_pairs, double *lats, double *lons,
**                           int **first_point, int **n_per_poly)
**
** Input:
**    n_pairs       - number of lat/lon pairs in source polygon
**    lats          - list of source latitudes
**    lons          - list of source longitudes
**
** Imput and Output:
**    none
**
** Output:
**    first_point   - array containing the starting data point location
**                    for each of the subsegments
**    n_per_poly    - array containing the number of pairs of lat/lons
**                    for each subsegment
** Return values:
**    n             - number of subsegments found
**
** Remarks:
**    This function allocates memory for both first_point and n_per_poly
** it is up to the calling function to free the memory when done with it.
**
**    This function is used to separate out counties that contain islands
** into multiple segments so the polygon filler and drawer won't puke.
*/

static int
  NumPolygon (
    int       n_pairs,
    double   *lats,
    double   *lons,
    int     **first_point,
    int     **n_per_poly)
{
  int              i = 1;   /* loop counter */
  int              n_segs;  /* number of segments found */
  int              first;   /* location of first point in new segment */
  double           f_lat;   /* first latitude for new segment */
  double           f_lon;   /* first longitude for new segment */

  first     = 0;
  f_lat     = lats[first];
  f_lon     = lons[first];

  *first_point = (int *) NULL;
  *n_per_poly  = (int *) NULL;

  n_segs    = 0;

  /*
   * loop through all of the pairs looking for pairs that match the
   * first pair in the segment. If one is found, treat it as a single
   * segment. if we are not through the entire list of pairs yet, any
   * future points will be considered a different segment. i know it
   * is confusing, but it is necessary for places like Door County,
   * Wisconsin that has 3 islands in one county.
   */

  while (i < n_pairs)
  {

    /*
     * if the current pair matches the lat/lon of the first pair
     * in this segment
     */

    if ((f_lat == lats[i] && f_lon == lons[i]) ||
        (n_segs != 0 && (i + 1) == n_pairs))
    {
      /* assign values for the starting location and number of pairs */

      *first_point = (int *) realloc
                        ((*first_point), sizeof (int) * (n_segs + 1));
      *n_per_poly  = (int *) realloc
                        ((*n_per_poly), sizeof (int) * (n_segs + 1));

      (*first_point)[n_segs] = first;
      (*n_per_poly)[n_segs]  = i - first + 1;

      n_segs++;

      /*
       * if we are not at the end of the list yet, reinitialize the
       * counters so we can go look for more segments
       */

      if (i != (n_pairs - 1))
      {
        i++;
        first = i;
        f_lat = lats[first];
        f_lon = lons[first];
      }

    }
    i++;
  }

  return (n_segs);
}

/*
** Name:
**    GetNavFrameNumber - get the appropriate frame number to use
**                        for navigation
**
** Interface:
**    static int
**    GetNavFrameNumber (const char *map, int def_frame, int image_frame_on,
**                       int *nav, int max)
**
** Input:
**    map         - map the user requested
**    def_frame   - default frame to use for nav
**    image_frame_on - image on flag
**    max         - max frame number allowed
**
** Input and Output:
**    none
**
** Output:
**    nav         - frame number to use for navigation
**
** Return values:
**    0           - success
**   <0           - failure
*/

static int
  GetNavFrameNumber (
    const char *map,
    int         def_frame,
    int         image_frame_on,
    int        *nav_frame_number,
    int         max_graphics_frame)
{
  const char           *t_string;   /* temp string for Mccmdstr() */
  int                   ok;         /* function return value */
  int                   value;      /* value from Mccmdint() */

  *nav_frame_number = def_frame;

  ok = Mccmdstr ("NAV", 1, "CURRENT", &t_string);
  if (ok < 0) { return (ok); }

  /* 
   * if the user specified CURRENT use the current image frame if the image
   * frame is on, otherwise the current graphics frame
   */

  if (t_string[0] == 'C')
  {
    if (strcmp (map, "SAT") == 0)
    {
      if (image_frame_on == TRUE)
      {
        *nav_frame_number = McGetImageFrameNumber();
      }
      else
      {
        *nav_frame_number = McGetGraphicsFrameNumber();
      }
    }
    return (0);
  }

  /* you cannot specify a map and NAV= */

  if (strcmp (map, "SAT") != 0)
  {
    Mceprintf ("You cannot specify NAV= with a map\n");
    return (-1);
  }

  /*
   * if the user specified something other that CURRENT, try to convert
   * it to a integer
   */

  ok = Mccmdint ("NAV", 1, "Navigation Frame", *nav_frame_number,
                 1, max_graphics_frame, &value);
  if (ok < 0) { return (ok); }

  /* assign new value */

  *nav_frame_number = value;

  return (0);
}

/*
** Name:
**    GetWatchBoxCenter - get the center line/elem of a watch box
**
** Interface:
**    static int
**    GetWatchBoxCenter (int nav_frame_number, double *lats, double *lons,
**                       int n_pairs, int *tvline, int *tvelem)
**
** Input:
**    nav_frame_number - navigation frame number
**    lats             - latitude points of box polygon
**    lons             - longitude points of box polygon
**    n_pairs          - number of lat/lon pairs of box polygon
**
** Input and Output:
**    none
**
** Output:
**    tvline      - tv line of box center
**    tvelem      - tv elem of box center
**
** Return values:
**    0           - success
**   -1           - invalid numberof lat/lon pairs
*/
static int
GetWatchBoxCenter(int nav_frame_number, double *lats, double *lons,
                  int n_pairs, int *tvline, int *tvelem)
{
    Fint4 dum[3];
    Fint4 line;
    Fint4 elem;
    Freal lat;
    Freal lon;

    int ok;
    int i;
    int tvlines_sum=0;
    int tvelems_sum=0;

    if (n_pairs < 1) return -1;

    for (i=0; i<n_pairs-1; i++)
    {
        dum[0] = 0;
        lat = (Freal)lats[i];
        lon = (Freal)lons[i];
        ok = illtv_((Fint4 *)&nav_frame_number, &dum[0], (Freal *)&lat,
                    (Freal *)&lon, &dum[0], &dum[1], &line,
                    &elem);

        tvlines_sum += line;
        tvelems_sum += elem;
    }

    *tvline = tvlines_sum / (n_pairs - 1);
    *tvelem = tvelems_sum / (n_pairs - 1);

    return 0;
}

/*
** Name:
**    DrawWatchBoxNumber - Draws the watch box number at a tvline/tvelem
**
** Interface:
**    static void
**    DrawWatchBoxNumber (int watch_number, int event_type, int tvline
**                        int tvelem, int *color_levs, int *height)
**
** Input:
**    watch_number - number of watch box
**    event_type   - event type (SEVWATCH or TORWATCH)
**    tvline       - tv line to plot number
**    tvelem       - tv elem to plot number
**    color_levs   - array of color levels for event_types
**    height       - height to plot number
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    none
**
** Remarks:
**    This function is typically called by the TraverseWatchTree() function.
**    That is why it returns void and contains a number of unnecessary
**    parameters.
*/
static void
DrawWatchBoxNumber(int watch_number, int event_type, int tvline, int tvelem,
                   int beg_date, int beg_time, int end_date, int end_time,
                   int *color_levs, int *height)
{

    char        *t_string;              /* temporary string */
    char        ctemp[24];              /* another temporary string */

    int dum;                    /* dummy variable */
    int plot_color;             /* color to plot watch number */


    GetPlotColor (event_type, color_levs, &plot_color, &dum);

    sprintf(ctemp, "%d", watch_number);

    if (event_type == TORNADO_WATCH)
    {
        t_string = stralloc("T", ctemp, (char *)NULL);
    }
    else if (event_type == TSTORM_WATCH)
    {
        t_string = stralloc("S", ctemp, (char *)NULL);
    }
    else return;

    McDrawText(t_string, strlen(t_string), plot_color, *height,
               tvline, tvelem, "C", "H", 2, 0);

    free(t_string);

    return;
}

/*
** Name:
**    DrawWatchBoxList - Draw a list of watch boxes and expire times on frame
**
** Interface:
**    static void
**    DrawWatchBoxList (int watch_number, int event_type, int tvline
**                      int tvelem, int beg_date, int beg_time,
**                      int end_date, int end_time,
**                      int *screen_domain, int *color_levs)
**
** Input:
**    watch_number  - number of watch box
**    event_type    - event type (SEVWATCH or TORWATCH)
**    tvline        - tv line to plot number
**    tvelem        - tv elem to plot number
**    beg_date      - issue date
**    beg_time      - issue time
**    end_date      - date of expiration
**    end_time      - time of expiration
**    screen_domain - screen domain
**    color_levs    - array of color levels for event_types
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    none
**
** Remarks:
**    Draws a list of watch box numbers across the top of the frame in
**    column/tabular form.
**    This function is typically called by the TraverseWatchTree() function.
**    That is why it returns void and contains a number of unnecessary
**    parameters.
*/
static void
DrawWatchBoxList (int number, int event_type, int cntr_line, int cntr_elem,
                  int beg_date, int beg_time, int end_date, int end_time,
                  int *screen_domain, int *color_levs)
{

#define         MAXCOL  20      /* max number of columns                */
#define         MAXLIN  10      /* max number of lines per column       */
#define         MAXLEN  30      /* max length in pixels of each column  */

char    *t_string;              /* temporary string                     */
char    ctemp[255];             /* another temporary string             */

int     dum;                    /* dummy variable                       */
int     day;                    /* day of watch expiration              */
int     month;                  /* month of watch expiration            */
int     year;                   /* year of watch expiration             */

int     plot_color;             /* plot color                           */

static int      column;         /* current column                       */
static int      ele;            /* current element to draw text         */
static int      height=7;       /* height to plot text                  */
static int      lin;            /* current line to draw text            */
static int      minele;         /* min element to draw text             */
static int      maxele;         /* max element to draw text             */
static int      minlin;         /* min line to draw text                */
static int      maxlin;         /* max line to draw text                */
static int      maxlen;         /* max len in pixels of each text block */
static int      n_columns;      /* number columns of maxlen that will fit
                                 * on the frame                         */
static int      text_line;      /* current count of lines written to this
                                 * column                               */
static int      first_time=0;   /* first call to this function flag     */

    /* Set up some static variables if this is the first call */
    if (first_time == 0)
    {
        minlin = screen_domain[0];
        maxlin = screen_domain[1];
        minele = screen_domain[2];
        maxele = screen_domain[3];

        /*
         * Get the number of pixels needed to draw each entry in the list
         * and compute the number columns we can use (varies by font)
        */
        maxlen = M0StrToPixLen("0000 00/00 00:00Z    ", (char *)NULL, &height) 
+ 10;
        n_columns = (maxele - minele) / maxlen;
        if (n_columns > MAXCOL) n_columns = MAXCOL;

        column    = -1;
        text_line = MAXLIN;

        first_time = 1;
    }

    text_line++;

    /*
     * See if we will go into the next text column.  If so, reset the line
     * and elements and draw the column header
     */
    if (text_line > MAXLIN)
    {
        column++;
        if (column > MAXCOL) return;

        text_line = 0;
        lin = minlin + 3;
        ele = column * maxlen + minele + 2;

        t_string = stralloc(" Watch Expiration", (char *)NULL);
        McDrawText(t_string, strlen(t_string), 5, height, lin, ele,
                   "B", "H", 0, 0);
        free(t_string);

        lin += 6;
        text_line = 1;
    }

    /*
     * Compute the new line number to draw text for this event and get the
     * plot color based on the event_type
     */
    lin += height + 3;
    GetPlotColor(event_type, color_levs, &plot_color, &dum);

    if (event_type == TSTORM_WATCH)
    {
        sprintf(ctemp, "%5d  ", number);
    }
    else if (event_type == TORNADO_WATCH)
    {
        sprintf(ctemp, "%5d  ", number);
    }
    else
    {
        Mcprintf("Not plotting %5d to the list\n",number);
        return;
    }

    /* Create and draw the expiration date/time text string */
    Mccydtodmy (end_date, &day, &month, &year);
    t_string = stralloc(ctemp, (char *)NULL);
    sprintf(ctemp, " %2d/%2d %02d:%02dZ",
            month, day, end_time/10000, (end_time/100)%100);
    t_string = stralloc (t_string, ctemp, " ", (char *)NULL);
    McDrawText (t_string, strlen(t_string), plot_color, height,
                lin, ele, "B", "H", 0, 0);
    free(t_string);

    return;
}

/*
** Name:
**    AddToWatchTree - Add a watch event to the watch binary tree
**
** Interface:
**    static int
**    AddToWatchTree (WATCHTREE **tree, int number, int event_type
**                    int cntr_line, int cntr_elem,
**                    int beg_date, int beg_time,
**                    int end_date, int end_time)
**
** Input:
**    tree          - tree structure
**    number        - watch number
**    event_type    - event type (ie. SEVWATCH or TORWATCH)
**    cntr_line     - tv line at center of watch box
**    cntr_elem     - tv elem at center of watch box
**    beg_date
**    beg_time
**    end_date
**    end_time
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    1  -     watch already exists
**    0  -     ok
**    -1 -     internal malloc error
**
** Remarks:
**    This function adds a watch to the WATCHTREE structure.  Duplicate
**    watches are not added. The tree is balanced by the watch number (ie.
**    a traversal of the tree will proceed in watch number order.  Call the
**    function TraverseWatchTree() to traverse the tree.
*/
static int
AddToWatchTree(WATCHTREE **node, int number, int type, int cntr_line,
               int cntr_elem, int beg_date, int beg_time,
               int end_date, int end_time)
{

WATCHTREE       *T;

    T=*node;
    /*
     * If we've reached the end of the tree, make the node
     */
    if (T == NULL)
    {
        T = (WATCHTREE *)malloc (sizeof(WATCHTREE));
        if (T == (WATCHTREE *)NULL) return -1;

        T->left = (WATCHTREE *)NULL;
        T->right = (WATCHTREE *)NULL;

        T->number       = number;
        T->type         = type;
        T->cntr_line    = cntr_line;
        T->cntr_elem    = cntr_elem;
        T->beg_date     = beg_date;
        T->beg_time     = beg_time;
        T->end_date     = end_date;
        T->end_time     = end_time;

        *node = T;
        return 0;
    }

    /*
     * Look for the position of the new node.  If the watch number already
     * exists in the tree, then return
     */

    if (number < T->number)
    {
        AddToWatchTree(&(T->left), number, type, cntr_line, cntr_elem,
                       beg_date, beg_time, end_date, end_time);
    }
    else if (number > T->number)
    {
        AddToWatchTree(&(T->right), number, type, cntr_line, cntr_elem,
                       beg_date, beg_time, end_date, end_time);
    }
    else return 1;

    return 0;
}

/*
** Name:
**    TraverseWatchTree - Traverse the watch binary tree
**
** Interface:
**    static int
**    TraverseWatchTree (WATCHTREE *tree, (Visit *)(int number, int event_type
**                        int cntr_line, int cntr_elem, int beg_date, int 
beg_time,
**                        int end_date, int end_time, int *dummy1, int *dummy2),
**                        int *dummy1, int *dummy2)
**
** Input:
**    tree          - pointer to tree structure
**    Visit         - function to visit at every node in tree
**    dummy1        - pointer to dummy variable that is passed to Visit()
**    dummy2        - pointer to dummy variable that is passed to Visit()
**
** Input and Output:
**    none
**
** Output:
**    none
**
** Return values:
**    none
**
** Remarks:
**    This function traverses the WATCHTREE binary tree structure and calls
**    the Visit() function at every node.  dummy1 and dummy2 are pointers to
**    variables that the traverse function passes to the visit function.  This
**    provides a mechanism for the calling function to pass additional variables
**    to the Visit() function.
*/
static void
TraverseWatchTree( WATCHTREE *tree, void (*Visit)(int, int, int, int, int, int,
                   int, int, int*, int *), int *dummy1, int *dummy2)
{

    if (tree != NULL)
    {
        TraverseWatchTree(tree->left, Visit, dummy1, dummy2);
        Visit(tree->number, tree->type, tree->cntr_line, tree->cntr_elem,
              tree->beg_date, tree->beg_time, tree->end_date, tree->end_time,
              dummy1, dummy2);
        TraverseWatchTree(tree->right, Visit, dummy1, dummy2);
    }
}

/*
** Name:
**    GetFrameMinMax - Retrieve min/max lat/lon of the frame
**
** Interface:
**    static int
**    GetFrameMinMax (int frame, double *minlat, double *maxlat,
**                        double *minlon, double *maxlon)
**
** Input:
**    frame         - frame number with navigation
**
** Input and Output:
**    minlat        - mininum latitude on frame
**    maxlat        - maximum latitude on frame
**    minlon        - minimum longitude on frame
**    maxlon        - maximum longitude on frame
**
** Output:
**    none
**
** Return values:
**    none
**
** Remarks:
**    none
*/
static int
GetFrameMinMax(int frame, double *minlat, double *maxlat,
                          double *minlon, double *maxlon)
{

Freal   fminlat;
Freal   fmaxlat;
Freal   fminlon;
Freal   fmaxlon;

int     ok;

    /*
     * fmnmx calls mapdef which looks for a IMA= keyword. IMA is not a valid
     * wwdisp keyword and the default is gotten from UC(-1), so poke the nav
     * frame number there
     */

    Mcpuc (frame, -1);

    ok = (int) fmnmx_ ((Fint *)&frame, &fminlat, &fmaxlat, &fminlon, &fmaxlon);

    *minlat = fminlat;
    *maxlat = fmaxlat;
    *minlon = fminlon;
    *maxlon = fmaxlon;

    return (ok);
}