
/*
 *   THIS FILE IS UNDER RCS - DO NOT MODIFY UNLESS YOU HAVE
 *   CHECKED IT OUT USING THE COMMAND CHECKOUT.
 *
 *    $Id: draw_map.c,v 1.7 2000/05/31 16:48:53 lucky Exp $
 *    Revision history:
 *
 *    $Log: draw_map.c,v $
 *    Revision 1.7  2000/05/31 16:48:53  lucky
 *    Modified to reuse code in the review/alarm applications
 *
 *    Revision 1.6  2000/02/15 19:27:07  lucky
 *    *** empty log message ***
 *
 *    Revision 1.5  2000/01/07 18:22:30  davidk
 *    changed the status line that appears at the bottom of the page when you move
 *    the mouse over an event, so that it is easier to read, including human
 *    readable time.
 *
 *    Revision 1.4  1999/11/09 16:53:02  lucky
 *    *** empty log message ***
 *
 *    Revision 1.3  1999/11/04 21:20:47  davidk
 *    added scalable earthquake sizes based on Lat size of the map, and added different
 *    symbols for earthquakes based on the depth of the quake. (square,circle,diamond).
 *
 *    Revision 1.1  1999/10/25 18:52:32  davidk
 *    Initial revision
 *
 *    Revision 1.8  1999/10/01 23:46:24  davidk
 *    updated getlist interface to contain 2 color coded modes and included
 *    support for recentering a map
 *
 *    Revision 1.7  1999/09/24 06:16:51  davidk
 *    added config file params for maxevents,numberofdays,default click effect.  Done in conjunction
 *    >> with the change in getlist, to do away with the get_events.html page, which means incorporating
 *    >> criteria into the getlist program via the config file and html-displayed page
 *
 *    Revision 1.6  1999/06/11 19:50:46  lucky
 *    Done working on the Station information page for now.
 *
 *    Revision 1.5  1999/06/08 20:25:45  lucky
 *    AreaBuf for stations has been modified so that it calls
 *    a page which indicates that this feature is not yet implemented.
 *    Before, clicking on a station would result in an error being
 *    reported by the browser.
 *
 *    Revision 1.4  1999/06/04 17:57:41  lucky
 *    more minor adjustments needed to make DrawMap work with other modules.
 *
 *    Revision 1.3  1999/06/04 15:57:37  lucky
 *    Added functions to this file so that DrawMap can be completely
 *    separated from getlist.c
 *
 *    Revision 1.2  1999/06/03 23:15:19  lucky
 *    *** empty log message ***
 *
 *    Revision 1.1  1999/06/02 21:42:55  lucky
 *    Initial revision
 *
 *
 *
 */
  
/* filename:       draw_map.c 
   module:         getlist
   platforms:      solaris 2.6 (& maybe NT 4)
   date:           06/02/1999
   author:         Lucky Vidmar

   description:   

   The functions in this file draw a map showing stations and
   events retrieved from an RDBMS. All of the database manipulation,
   map intelligence, and additional web page enhancements are done
   outside of these routines. Our only task is to return an image
   drawn using the GD library tools developed by Tom Boutell, available 
   at  http://www.boutell.com/gd/.
   
   Before calling DrawMap (), a map must first be generated by calls
   to the Map Server, implemented in functions in the mapserver.o object, 
   whose source (and API description) is in phaseIII/src/mapserver

   The maps are currently generated by GMT, a set of map tools developed 
   by Smith & Wessel and is available from http://www.soest.hawaii.edu/gmt/.  

/* this includes structs neccessary for processing
   maps and state logic for the web displays */
#include <map_display_structs.h>

/* our very own header file for function prototypes
   and mapping constants */
#include "getlist.h"

/* the headers for the mapserver interface */
#include <p3_mapserver.h>

/* the headers for time functions that convert to/from ascii*/
#include <p3_time_functions.h>

/************* END OF #INCLUDES *************/


/* Magnitude to Pixel conversion array
   This is how we figure out how big of
   a square to draw based on the Magnitude
   of a quake.
***************/
int Mag2Pix[][9]=
{
/*  {1,2,3,5,8,14,22,30,34},*/
  {1,2,4,6,8,14,22,30,34},
  {1,1,2,3,6,10,16,22,25},
  {1,1,1,2,4, 7,11,15,17}
};

#define FINAL_GIF_NAME "../html/image_%d.gif"


/*** Function prototypes ***/

int 	GetMapFromMapServer(LocationProjectionStruct *, 
				                      MapServerImageStruct *);


static int DrawEqsInit (char *, gdImagePtr *, int);
static int PlotQuakesOnIndex (gdImagePtr, P3DB_EventListStruct *, int, EQSPicture *, 
					char *, char *, LocationProjectionStruct *, int);
int PlotStations (gdImagePtr, P3DB_StationStruct *, int, EQSPicture *, 
					char *, char *, LocationProjectionStruct *, int, int);
static int Timestamp_gdImage_AndConvert2GIF (EQSPicture *, gdImagePtr, int);
static int LatLon2Pix (LocationProjectionStruct *, int *, int *,
					float, float);



/******************************************************
  Function:    DrawMap ()
  Purpose:     Draw map showing events and stations

  Parameters:    
      Input
      pStations:
               Pointer to the list of stations to draw.
      NumStations:
               Number of stations to draw.
      pEvents:
               Pointer to the list of events to draw.
      NumEvents:
               Number of events to draw.
      pPic:
               Coordinates in both pixels and lat/lon degrees of
               the image to draw on
      pLPSData:
               Pointer to the Location/Projection data for the
               current map.
      DisplayStationsFlag:
               If 1 display stations no matter what, if 0 display
               stations only if we are within the MaxStationDisplayWidth.
      MaxStationDisplayWidth:
               Width in degrees below which we display stations.
      ImageFileName:
               Name of the GMT generated map used as a base for
               drawing events and stations.
      station_color:
               Color to use for drawing stations.
      initialize:
               If 0 DrawEqsInit will NOT be called.
      convert2gif:
               If 1 TimeStampAndConvert will be called, otherwise
               it is assumed that more calls to DrawMap will be made
               and the conversion will happen in the last call.
      debug_flag:
               Print debug info if not 0.

      Input/Output
      pImage:  pointer to a gdlib image.  used as a handle for
               the image on which to draw.

      Output
      AreaBuf: 
               Character buffer in which to store an htmlized list
               of the events.  Both AreaBuf and AreaBuf2 come in
               blank but pre-malloced.
      AreaBuf2:
               Temporary buffer in which to store an htmlized list
               of the events.
      
  Author:
               Lucky Vidmar, 06/02/1999

  Internal Functions Called:
    DrawEqsInit(), PlotStations(), PlotQuakesOnIndex(),
    Timestamp_gdImage_AndConvert2GIF()
               
  Library Functions Called: 
               
  External Library Functions Called:
  **********************************************************/
int		DrawMap (gdImagePtr *pImage, P3DB_StationStruct *pStations, 
					int NumStations, P3DB_EventListStruct *pEvents, int NumEvents, 
					EQSPicture *pPic, LocationProjectionStruct *pLPSData,
					int DisplayStationsFlag, float MaxStationDisplayWidth,
					char *ImageFileName, char *AreaBuf, char *AreaBuf2,
					int *station_color, int initialize, int convert2gif, 
                    int debug_flag)
{

  int RetVal;

  logit("","DrawMap(): at top, NumStations=%d, NumEvents=%d\n",
        NumStations,NumEvents);

  if (initialize != 0)
  {
    if(debug_flag) /* DK 1/9/99*/
      logit("","Calling DrawEqsInit. pImage is %ld. debug_flag is %d \n", 
					*pImage, debug_flag);

    /* Initialize the GIF drawing environment */
    RetVal=DrawEqsInit(ImageFileName, pImage, debug_flag);
  
    if(debug_flag) /* DK 1/9/99*/
      logit("","Returned from DrawEqsInit. pImage is %ld.\n", *pImage);

    if(RetVal)
    {
      logit("","draw_eqs_init failed. Quitting.\n");
      printf("draw_eqs_init failed. Quitting.</html>\n");
      return(-1);
    }
  }

  /* If our map is of the prescribed size and the station display
     switch is set to auto, or if the station display switch is set
     to on, then go show them stations that were passed in. */
  if(((pPic->deg[PIC_TOP] - pPic->deg[PIC_BOT] <= MaxStationDisplayWidth)
	  && (DisplayStationsFlag == P3_STATION_DISPLAY_DEFAULT))
		  || DisplayStationsFlag == P3_STATION_DISPLAY_STATIONS)
  {
    /* add stations to the Quake Map */
    if(debug_flag)
      logit("","Calling PlotStations().\n");
    
    RetVal=PlotStations(*pImage, pStations, NumStations,
   	                    pPic,AreaBuf,AreaBuf2,pLPSData, 
						*station_color, debug_flag);
    if(debug_flag)
      logit("","Returned from PlotStations().\n");

    if(RetVal)
    {
      logit("","PlotStations failed. Quitting.\n");
      printf("PlotStations failed. Quitting.</html>\n");
      return(-1);
    }

  }     /* end if stations should be displayed */
  

  /* Create the Quake Map */
  if(debug_flag)
    logit("","Calling PlotQuakesOnIndex().\n");
  
  RetVal=PlotQuakesOnIndex(*pImage, pEvents, NumEvents,
                              pPic,AreaBuf,AreaBuf2,pLPSData, debug_flag);
  if(debug_flag)
     logit("","Returned from PlotQuakesOnIndex().\n");

  if(RetVal)
  {
    logit("","plot_quakes_on_index failed. Quitting.\n");
    printf("plot_quakes_on_index failed. Quitting.</html>\n");
    return(-1);
  }


  if (convert2gif == 1)
  {

    if(debug_flag)
      logit("","Calling TimeStampAndConvert().\n");
  
    RetVal=Timestamp_gdImage_AndConvert2GIF(pPic, *pImage, debug_flag);

    if(debug_flag)
      logit("","Returned from TimeStampAndConvert().\n");

    if(RetVal)
    {
      logit("","TimeStampAndConvert failed. Quitting.\n");
      printf("TimeStampAndConvert failed. Quitting.</html>\n");
      return(-1);
    }

  }

  /* We have successfully produced the final map.  So please
     delete the basemap now.  */
  if(remove(ImageFileName))
  {
    logit("","Error Deleting temporary file %s.\n", ImageFileName);
  }

  return 0;

}

/******************************************************
  Function:    DrawEqsInit()
  Purpose:     Load a GIF image file into memory and get a pointer 
               to it via the gdlib routines.  Initialize any colors
               and other drawing properties needed to use the 
               gdlib routines to draw on the image.
  Parameters:    
      Input
      szBaseMapName: 
               the name of the GIF image file to load.
      Output
      pImage:
               the gdImagePtr to the GIF file that we draw
               our events and stations on, is created using
               a GIF basemap named szBaseMapName
      debug_flag:
               should we print out debug messages.


  Author:
               DK, before 04/15/1999

  Internal Functions Called:

  Library Functions Called: 
               logit()

  EW Library Functions Called:

  External Library Functions Called:
               gdImageCreateFromGif(),
  System Functions Called:  
               fopen()
  **********************************************************/
static int DrawEqsInit(char * szBaseMapName, gdImagePtr *pImage, int debug_flag)
{

  FILE * fIn;

  if(debug_flag) /* DK 1/9/99*/
	  logit("","DrawEqsInit:opening BaseMap %s.\n",szBaseMapName);

	fIn = fopen(szBaseMapName, "rb"); 

	if (!fIn) {
		logit("et", "getlist:DrawEqsInit():Can't load source image;\n"
          "this program is much more impressive if %s is available\n",
          szBaseMapName);
		*pImage = 0;
    return(-1);
	} 
  else 
  {
    if(debug_flag) /* DK 1/9/99*/
      logit("","calling gdImageCreateFromGif().\n");
  
    /* Create a usable image from the GIF */
		*pImage = gdImageCreateFromGif(fIn);

    if(debug_flag) /* DK 1/9/99*/
	    logit("","Image created from GIF. pImage is %ld.\n", *pImage);

    /* First allocate patriotic colors. */
    MC_red = gdImageColorAllocate(*pImage, 255, 0, 0);
    MC_white = gdImageColorAllocate(*pImage, 255, 255, 255);
    MC_blue = gdImageColorAllocate(*pImage, 0, 0, 255);

    /* Then those other not so important colors */
    MC_yellow = gdImageColorAllocate(*pImage, 255, 255, 0);
    MC_black = gdImageColorAllocate(*pImage, 0, 0, 0);
    MC_triangle = gdImageColorAllocate(*pImage, 241, 183, 125);
    MC_green = gdImageColorAllocate(*pImage, 0, 255,0);

	fclose(fIn);
    return(0);
  }
}  /* End of DrawEqsInit() */


/******************************************************
  Function:    PlotQuakesOnIndex()
  Purpose:     Plot the earthquakes in the pQuakes list onto a GIF basemap 
               image using the gdlib routines to draw.  Also create a
               client-side web imagemap string that lists all of the quakes
               and there pixel coordinates on the new map.
  Parameters:    
      Input
      pQuakes: pointer to a list of Events retrieved from the RDBMS
      NumEqs:  number of quakes in the pQuakes list
      pPic:    the coordinates in both pixels and lat/lon degrees of 
               the image to draw on
      pLPSData:
               a pointer to the Location/Projection data for the
               current map.

      Input/Output
      pImage:  pointer to a gdlib image.  used as a handle for
               the image on which to draw.

      Output
      AreaBuf: a character buffer in which to store an htmlized list
               of the events.  Both AreaBuf and AreaBuf2 come in 
               blank but pre-malloced.
      AreaBuf2:
               a temporary buffer in which to store an htmlized list
               of the events.
  Author:
               DK, before 04/15/1999

  Internal Functions Called:
               LatLon2Pix()

  Library Functions Called: 
               logit()

  EW Library Functions Called:
               

  External Library Functions Called:
               gdImageFilledRectangle(),gdImageRectangle()
  System Functions Called:  
               time(),strcpy(),sprintf(),strcat()
  **********************************************************/
static int PlotQuakesOnIndex(gdImagePtr pImage, P3DB_EventListStruct * pQuakes, 
						int NumEqs, EQSPicture * pPic, char * AreaBuf, 
						char * AreaBuf2, LocationProjectionStruct * pLPSData, 
						int debug_flag)
{
  float eqlat,eqlon,eqmag,eqz;
  int eqot,eqid,eqmagint,eqmaggroup;
  int pleft,pright,ptop,pbottom;
  float dleft,dright,dtop,dbottom;
  int k,hrdiff,fill_color,halfhtpix;
  int xPix,yPix;
  time_t now;
  gdPoint pPoints[POINTS_IN_DIAMOND];
  char szTemp[30];

# define SQRT_2 1.414

  /* Get Time */
  time(&now);


  /* Create shortcuts for pixel border location */
  pleft=pPic->pix[PIC_LEFT];
  pright=pPic->pix[PIC_RIGHT];
  ptop=pPic->pix[PIC_TOP];
  pbottom=pPic->pix[PIC_BOT];

  /* Create shortcuts for degree border location */
  dleft=pPic->deg[PIC_LEFT];
  dright=pPic->deg[PIC_RIGHT];
  dtop=pPic->deg[PIC_TOP];
  dbottom=pPic->deg[PIC_BOT];

  /* Quakes come in reverse time order, so go through them
     backwards, so that the newest quakes will be drawn last
     and thus be on top.  DK 041599 */

  for (k=NumEqs-1;k>=0;k--) 
  {
    /* get the lat,lon,magnitude,origin time, eventid, 
       and depth of each quake */
    eqlon =  pQuakes[k].Lon;
    eqlat =  pQuakes[k].Lat;
    eqmag =  pQuakes[k].Magnitude;
    eqot = (int)(pQuakes[k].OT);
    eqid = pQuakes[k].idEvent;
    eqz = pQuakes[k].Depth;

    /* Find pixel location of earthquake on index map. */
    LatLon2Pix(pLPSData,&xPix,&yPix,eqlat,eqlon);

    /* Calculate the age of the event in hours. */
    hrdiff=(now-eqot) / 3600;

    /* Assign color based on age of earthquake. */
    /* red < 1 hour ago
       blue < 1 day ago
       yellow < 1 week ago
       white >= 1 week ago
    */
    if(hrdiff ==0)
      fill_color = MC_red;
    else if(hrdiff < 24)
      fill_color = MC_blue;
    else if(hrdiff < (24*7))
      fill_color = MC_yellow;
    else
      fill_color = MC_white;

    /* Scale and locate square plot symbol. */
    eqmagint=(int)eqmag;
    if(eqmagint > 7)
    {
      /* we only go up to 7 for drawing size, 
         it makes a pretty big square */
      if(eqmagint <= 10)
        eqmagint=7;
      else
      {
        logit("et","PlotQuakesOnIndex:1:EQ with "
              "bad magnitude encountered: %2.2f.\n",eqmag);
        eqmagint=0;
        eqmag=0;
      }
    }
    if(eqmagint < 0)
    {
        logit("et","PlotQuakesOnIndex:2:EQ with "
              "bad magnitude encountered: %2.2f.\n",eqmag);
        eqmagint=0;
        eqmag=0;
    }

    /* Copy the current html  <AREA> buffer to the 2nd location */
    strcpy(AreaBuf2,AreaBuf);

    /* figure out what range of quake box sizes we want to use.
       we choose a smaller relative size for larger maps */
    if(pLPSData->MapData.LatHeight <= 40)
      eqmaggroup=0;
    else if(pLPSData->MapData.LatHeight <= 100)
      eqmaggroup=1;
    else 
      eqmaggroup=2;

    halfhtpix=(int)(Mag2Pix[eqmaggroup][eqmagint]+ 
      (eqmag - eqmagint) * (Mag2Pix[eqmaggroup][eqmagint+1] - Mag2Pix[eqmaggroup][eqmagint]));

    if(eqz < 70)
    {
      gdImageFilledRectangle(pImage, xPix-halfhtpix,yPix-halfhtpix,
        xPix+halfhtpix,yPix+halfhtpix,fill_color);
      gdImageRectangle(pImage, xPix-halfhtpix,yPix-halfhtpix,
        xPix+halfhtpix,yPix+halfhtpix,MC_black);
    /* This code adapted from www.ibm.com's use of image maps 1998/08/05 DK */ 
    /* Record the just plotted quake in the imagemap string */
    /* We need to put the events(quakes) into the string in the reverse order
       so that the latest quakes get layered over any overlapping 
       older quakes */
    sprintf(AreaBuf,"<AREA SHAPE=\"RECT\" COORDS=\"%d,%d %d,%d\" "
                "HREF=\"eqparam2html"EXE_EXT"?EVENT%d=On\" "
                "onMouseOver=\"self.status='Lat %.3f,  Lon %.3f,  Z %.3f,  Mag %.2f,  "
                "OT(%s), %d, %d';"
                " return true\">\n",
                xPix-halfhtpix,yPix-halfhtpix,
                xPix+halfhtpix,yPix+halfhtpix,
                eqid,eqlat,eqlon,eqz,eqmag,P3_ttoa((time_t *)&eqot,szTemp),xPix,yPix);
    }
    else if(eqz < 350)
    {
      gdImageArc(pImage, xPix,yPix, 2*halfhtpix,2*halfhtpix, 0, 359, MC_green);
      gdImageFillToBorder(pImage, xPix,yPix,MC_green,fill_color);
      gdImageArc(pImage, xPix,yPix, 2*halfhtpix,2*halfhtpix, 0, 359, MC_black);
      sprintf(AreaBuf,"<AREA SHAPE=\"CIRCLE\" COORDS=\"%d,%d,%d\" "
              "HREF=\"eqparam2html"EXE_EXT"?EVENT%d=On\" "
              "onMouseOver=\"self.status='Lat %.3f,  Lon %.3f,  Z %.3f,  Mag %.2f,  "
              "OT(%s), %d, %d';"
              " return true\">\n",
              xPix,yPix,halfhtpix,
              eqid,eqlat,eqlon,eqz,eqmag,P3_ttoa((time_t *)&eqot,szTemp),xPix,yPix);
    }
    else
    {
      /* Create point locations for diamond */
      pPoints[0].x=xPix - (int)(halfhtpix*SQRT_2);
      pPoints[0].y=yPix;
      pPoints[1].x=xPix;
      pPoints[1].y=yPix - (int)(halfhtpix*SQRT_2);
      pPoints[2].x=xPix + (int)(halfhtpix*SQRT_2);
      pPoints[2].y=yPix;
      pPoints[3].x=xPix;
      pPoints[3].y=yPix + (int)(halfhtpix*SQRT_2);

      gdImageFilledPolygon(pImage, pPoints, POINTS_IN_DIAMOND, fill_color);
      gdImagePolygon(pImage, pPoints, POINTS_IN_DIAMOND, MC_black);
      sprintf(AreaBuf,"<AREA SHAPE=\"POLY\" COORDS=\"%d,%d %d,%d %d,%d %d,%d\" "
              "HREF=\"eqparam2html"EXE_EXT"?EVENT%d=On\" "
              "onMouseOver=\"self.status='Lat %.3f,  Lon %.3f,  Z %.3f,  Mag %.2f,  "
              "OT(%s), %d, %d';"
              " return true\">\n",
              pPoints[0].x,pPoints[0].y,pPoints[1].x,pPoints[1].y,
              pPoints[2].x,pPoints[2].y,pPoints[3].x,pPoints[3].y,
              eqid,eqlat,eqlon,eqz,eqmag,P3_ttoa((time_t *)&eqot,szTemp),xPix,yPix);
    }


    /* concatinate the old buffer contents onto the back of the new contents */
    strcat(AreaBuf,AreaBuf2);
  }

  return(0);
}  /* End of PlotQuakesOnIndex() */

/******************************************************
  Function:    PlotStations()
  Purpose:     Plot the stations in the pStations list onto a GIF basemap 
               image using the gdlib routines to draw.  Also create a
               client-side web imagemap string that lists all of the stations
               and there pixel coordinates on the new map.
  Parameters:    
      Input
      pStations: pointer to a list of Stations retrieved from the RDBMS
      NumEqs:  number of stations in the pStations list
      pPic:    the coordinates in both pixels and lat/lon degrees of 
               the image to draw on
      pLPSData:
               a pointer to the Location/Projection data for the
               current map (that we want to draw on).

      Input/Output
      pImage:  pointer to a gdlib image.  used as a handle for
               the image on which to draw.

      Output
      AreaBuf: a character buffer in which to store an htmlized list
               of the stations for clientside imagemapping.  
      AreaBuf2:
               a temporary buffer in which to store an htmlized list
               of the events.
  Author:
               DK, before 04/15/1999

  Internal Functions Called:
               LatLon2Pix()

  Library Functions Called: 
               logit()

  EW Library Functions Called:
               

  External Library Functions Called:
               gdImageFilledPolygon(),gdImagePolygon()
  System Functions Called:  
               time(),strcpy(),sprintf(),strcat()

  *Note:  We blindly assume that AreaBuf and AreaBuf2 are big 
  enough for our needs, and we don't worry about them overflowing.
  **********************************************************/
int PlotStations(gdImagePtr pImage, P3DB_StationStruct * pStations, 
						int NumStations, EQSPicture * pPic, char * AreaBuf, 
						char * AreaBuf2, LocationProjectionStruct * pLPSData,
						int station_color, int debug_flag)
{
  float stalat,stalon,staelev;
  char sta[7],chan[9],net[9],loc[9];
  int staidcomp,staidchan;
  int pleft,pright,ptop,pbottom;
  float dleft,dright,dtop,dbottom;
  int k,fill_color;
  int xPix,yPix;
  gdPoint pPoints[POINTS_IN_TRIANGLE];

  if(debug_flag)
    logit("","Entered PlotStations. pImage is %ld.\n", pImage);

  /* Create shortcuts for pixel border location */
  pleft=pPic->pix[PIC_LEFT];
  pright=pPic->pix[PIC_RIGHT];
  ptop=pPic->pix[PIC_TOP];
  pbottom=pPic->pix[PIC_BOT];

  /* Create shortcuts for degree border location */
  dleft=pPic->deg[PIC_LEFT];
  dright=pPic->deg[PIC_RIGHT];
  dtop=pPic->deg[PIC_TOP];
  dbottom=pPic->deg[PIC_BOT];

  for (k=0;k<NumStations;k++) 
  {

    stalon =  pStations[k].Lon;
    stalat =  pStations[k].Lat;
    staelev = pStations[k].Elev;
    strcpy(sta,pStations[k].Sta);
    strcpy(chan,pStations[k].Comp);
    strcpy(net,pStations[k].Net);
    strcpy(loc,pStations[k].Loc);
    staidchan = pStations[k].idChan;
    staidcomp = pStations[k].idComp;


    /* Find pixel location of earthquake on index map. */
    LatLon2Pix(pLPSData,&xPix,&yPix,stalat,stalon);

    /* Scale and locate triangle plot symbol. */

    /* Create point locations for triangle */
    pPoints[0].x=xPix - 0;
    pPoints[0].y=yPix - TRI_DIST_UP;
    pPoints[1].x=xPix + TRI_DIST_HORIZ;
    pPoints[1].y=yPix + TRI_DIST_DOWN;
    pPoints[2].x=xPix - TRI_DIST_HORIZ;
    pPoints[2].y=yPix + TRI_DIST_DOWN;

    fill_color = station_color;

    gdImageFilledPolygon(pImage, pPoints, POINTS_IN_TRIANGLE, fill_color);
    gdImagePolygon(pImage, pPoints, POINTS_IN_TRIANGLE, MC_black);

    /* This code adapted from www.ibm.com's use of image maps 1998/08/05 DK */ 
    strcpy(AreaBuf2,AreaBuf);

#ifdef OLD
    sprintf(AreaBuf,"<AREA SHAPE=\"POLY\" COORDS=\"%d,%d,%d,%d,%d,%d\" "
            "HREF=\"stainfo?%s,%s,%s,\" "
            /*" HREF=\"eqparam2html"EXE_EXT"?EVENT%d=On\" " */
            "onMouseOver=\"self.status='Sta %s, Net %s, Lat %.2f,Lon %.2f, Elev %.2f';"
            " return true\">\n",
            pPoints[0].x,pPoints[0].y,pPoints[1].x,pPoints[1].y,
            pPoints[2].x,pPoints[2].y,
            sta, chan, net, sta,net,stalat,stalon,staelev);
#endif OLD
    sprintf(AreaBuf,"<AREA SHAPE=\"POLY\" COORDS=\"%d,%d,%d,%d,%d,%d\" "
            "HREF=\"stainfo?%d,\" "
            /*" HREF=\"eqparam2html"EXE_EXT"?EVENT%d=On\" " */
            "onMouseOver=\"self.status='Sta %s, Net %s, Lat %.2f,Lon %.2f, Elev %.2f';"
            " return true\">\n",
            pPoints[0].x,pPoints[0].y,pPoints[1].x,pPoints[1].y,
            pPoints[2].x,pPoints[2].y,
            staidchan, sta,net,stalat,stalon,staelev);

    strcat(AreaBuf,AreaBuf2);
  }  /* end of for k < NumStations */

  if(debug_flag)
    logit("","Leaving PlotStations().\n");

  return(0);
}  /* End of PlotStations() */



/******************************************************
  Function:    Timestamp_gdImage_AndConvert2GIF()
  Purpose:     Timestamp an image using the gdlib
               routines, and save the image as a GIF.
  Parameters:    
      Input
      pPic:   Coordinates for the image in Lat/Lon and Pixels
      
      Input/Output
      pImage:  Pointer to the image we are working with
  Author:
               DK, before 04/15/1999

  Internal Functions Called:
  Library Functions Called: 
               logit(),ttoa()
  EW Library Functions Called:
  External Library Functions Called:
               gdImageString(),gdImageInterlace(),
               gdImageGif(),gdImageDestroy()
  System Functions Called:  
               time(),strcat(),sprintf(),fopen(),fclose()
  **********************************************************/
static int Timestamp_gdImage_AndConvert2GIF(EQSPicture * pPic, 
							gdImagePtr pImage, int debug_flag)
{

  char fname[100];
  time_t now;
  char NowString[50];
  int pleft,pright,ptop,pbottom;
  FILE * fOut;

  /* Create shortcuts for pixel border location */
  pleft=pPic->pix[PIC_LEFT];
  pright=pPic->pix[PIC_RIGHT];
  ptop=pPic->pix[PIC_TOP];
  pbottom=pPic->pix[PIC_BOT];

  /* Write time string on top of image */
  time(&now);
  P3_ttoa(&now, NowString);
  gdImageString(pImage, gdFontGiant, pleft+28,pbottom-30, 
                strcat(NowString," UTC"), MC_red);

  /* Make output image interlaced (allows "fade in" in some viewers,
  and in the latest web browsers) */
  gdImageInterlace(pImage, 1);

  /* Open GIF file for writing */
  sprintf(fname,FINAL_GIF_NAME,getpid());
  fOut = fopen(fname, "wb");
  if(!fOut)
  {
    logit("","Could not open fname\n");
    return(-1);
  }

  /* Convert gdimage to GIF and save as a file */
  gdImageGif(pImage, fOut);

  /* Close written GIF file */
  fclose(fOut);

  /* Clean up */
  gdImageDestroy(pImage);

  return(0);
}  /* end Timestamp_gdImage_AndConvert2GIF() */



/******************************************************
  Function:    LatLon2Pix()
  Purpose:     Convert Lat/Lon coordinates on a given map to
               pixel coordinates, based on map parameters
               including size, projection type, and focal point
  Parameters:    
      Input
      pLPSData: 
               a pointer to the location and projection data
               for the map we are interpreting.
      Lat:    the Lat coordinate on the map
      Lon:    the Lon coordinate on the map

      Output
      pXPix:    a pointer to the resulting X coordinate of the
               Lat,Lon input coordinates.
      pYPix:    a pointer to the resulting Y coordinate of the
               Lat,Lon input coordinates.
  Author:
               DK, before 04/15/1999

  Internal Functions Called:
  Library Functions Called: 
  EW Library Functions Called:
  External Library Functions Called:
  System Functions Called:  

  *Note:  This function is currently VERY STUPID!!!!!!
          It has at most the intelligence of a consultant
  **********************************************************/
static int LatLon2Pix(LocationProjectionStruct * pLPSData,
               int* pXPix, int* pYPix,
               float Lat, float Lon)
{
  float PixsPerLonDeg,PixsPerLatDeg;
  int XPixCenter,YPixCenter;
  float TempLon;

  PixsPerLonDeg=pLPSData->PixelWidth/pLPSData->MapData.LonWidth;
  PixsPerLatDeg=pLPSData->PixelHeight/pLPSData->MapData.LatHeight;
  XPixCenter=P2_PIC_LEFT_PIX_BORDER + pLPSData->PixelWidth/2;
  YPixCenter=P2_PIC_TOP_PIX_BORDER + pLPSData->PixelHeight/2;
  if(pLPSData->MapData.CenterLon - Lon > 180)
  {
    TempLon=Lon+360;
  }
  else
  if(Lon - pLPSData->MapData.CenterLon > 180)
  {
    TempLon=Lon-360;
  }
  else
  {
    TempLon=Lon;
  }
  *pXPix=(int)(XPixCenter - (pLPSData->MapData.CenterLon-TempLon)*PixsPerLonDeg);
  *pYPix=(int)(YPixCenter + (pLPSData->MapData.CenterLat-Lat)*PixsPerLatDeg);

  logit("","CenterLon %.1f(%.1f,%.1f), Lat %.1f, Lon %.1f, TempLon %.1f, Pix(%d,%d)\n",
        pLPSData->MapData.CenterLon,pLPSData->MapData.LonWidth,pLPSData->MapData.LatHeight,
        Lat,Lon,TempLon,*pXPix,*pYPix);

  return(0);
}  /* End of LatLon2Pix() */




int GetMapFromMapServer(LocationProjectionStruct * pLPSData, 
                      MapServerImageStruct * pMSISdata)
/******************************************************
  Function:    GetMapFromMapServer()
  Purpose:     Create a map in an image format using the 
               PhaseIII mapserver library routines.
  Parameters:    
      Input
      pLPSData: 
               a pointer to the location and projection data
               for the map we want created.
      Output
      pMSISdata: 
               a pointer to image and file information for the
               map.  The image is created by the mapserver, and
               its information is outputted from this function.
  Author:
               DK, before 04/15/1999

  Internal Functions Called:
               
  Library Functions Called: 
               logit(),P2_EW_MS_InitMapStruct(),CreateMap()
  EW Library Functions Called:

  External Library Functions Called:
               
  System Functions Called:  
               fprintf(),putenv(),sprintf(),getpid(),getcwd()
  **********************************************************/
{

	P2_EW_MS_MapInfoStruct		*map;

	if ((map = P2_EW_MS_InitMapStruct()) == NULL)
	{
		fprintf (stderr, "Call to InitMapStruct failed.\n");
		return P3DB_RETURN_FAILURE;
	}

	/* This is the map */
	map->lat1 = pLPSData->MapData.CenterLat-pLPSData->MapData.LatHeight/2;
	map->lat2 = pLPSData->MapData.CenterLat+pLPSData->MapData.LatHeight/2;
	map->lon1 = pLPSData->MapData.CenterLon-pLPSData->MapData.LonWidth/2;
	map->lon2 = pLPSData->MapData.CenterLon+pLPSData->MapData.LonWidth/2;
	map->x_annot = pLPSData->MapData.LonWidth*2;
	map->x_grid = pLPSData->MapData.LonWidth/2;
	map->x_frame = map->x_grid;
	map->y_annot = pLPSData->MapData.LatHeight*2;
	map->y_grid = pLPSData->MapData.LatHeight/2;
	map->y_frame = map->y_grid;

	map->fill_shade = 100;
  map->lon_degrees_per_inch = pLPSData->MapData.LonWidth / pLPSData->PixelWidth 
                              * PIXELS_PER_INCH;
  map->lat_degrees_per_inch = pLPSData->MapData.LatHeight / pLPSData->PixelHeight
                              * PIXELS_PER_INCH;

  map->rivers=pLPSData->MapData.Rivers;
  map->boundaries=pLPSData->MapData.Politicals;
  if(!pLPSData->MapData.bBorder)
  {
    map->x_frame=0;
    map->y_frame=0;
  }
  if(!pLPSData->MapData.bLatLonLines)
  {
    map->x_grid=0;
    map->y_grid=0;
  }

  /* the HOME var is needed, but not used for writing by GMT */
  putenv("HOME=/opt/netscape");

  /* DK Cleanup:  ImageFileName was hardcoded to run_working, 
     I am changing it to be relative to run_$P3_VERSION/bin 
     DK 04/16/1999  */
  sprintf(pMSISdata->ImageFileName,
          "../html/P3GL_temp_image_%d.gif",
          getpid());

	if (P2_EW_MS_CreateMap (map, pMSISdata->ImageFileName) != P2_EW_MS_MAP_SUCCESS) 
	{
		fprintf (stderr, "Call to CreateMap failed.\n");
		return(-1);
	}

  return(0);
}  /* End of GetMapFromMapServer() */
