
/***************************************************************************
 * mapfunc.c --  Author: Ray R. Larson  - version 0.4, March 1993
 *  
 * The functions in this file implements a 
 * special routine to read and display WorldDataBank, stored as
 * a POSTGRES Inversion large object, file in a frame via tcl/tk .
 * Routines are included to identify the latitude and longitude of
 * a mouse click in the frame.
 *
 * Copyright 1993 Regents of the University of California.
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 **************************************************************************/

/* TK/Tcl includes */
#include "tk.h"

/* system includes */
#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <signal.h>
#include <sys/param.h>

/* Postgres/libpq includes */

#include "c.h"
#include "strings.h"
#include "tmp/libpq-fe.h"
#include "tmp/libpq-fs.h"
#include "utils/log.h"

#include <errno.h>

extern char dbnamebuffer[];
extern char *tcldbname;
extern int Debugging;
extern char *debug_file;
extern FILE *debug_port;


/* WDB map drawing variables */

#define	QSPAN	90*6000
#define HSPAN	QSPAN*2
#define WSPAN	QSPAN*4

typedef struct { short Code, Lat, Lon; } PNT ;
#define PNTSiz	sizeof(PNT)
/* allocate enough for the big map */
#define PCount  200000
int mapfileread = FALSE;
int mapfilepoints;
PNT	p[PCount];
long	Latitude, Longitude,
		X_Scale, Y_Scale,
		val,
		ymax,ymin,xmax,xmin,xspan,yspan;
double zoom;
char  *CTable[] = {
	"#000", "#ddd", "#0a0", "#d00", "#ff0", "#5f5", "#aaf", "#33f"
};

long colr[] = { 2, 2, 3, 0, 4, 5, 6, 7 }; /* GC 'pen' = line type DIV 1000 */



/******************** Draw WDB Map in a window **************************/
int
DrawMapCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
	char *fn;
	int centerlatdeg, centerlondeg, centerlatmin, centerlonmin, zoomval;
        int win_width, win_height, winw, winh;
        double aspect;
	GC gc[8];
	XColor *colorPtr;
	XGCValues values;
	unsigned int attmask;
        XSetWindowAttributes atts;
	int i, foo;
	Tk_Window tkwin2;
	register int x, y;
	register PNT	*pp;
	PNT	*pend;
	int filein, gcindex;
	int	z,LonPrv=0,LatPrv=0,xprv=0,yprv;
	int	newseg=0;


	if (argc < 8) {
		Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			" window_name mapfile latitude_degrees latitude_minutes ",
                        "longitude_degrees longitude_minutes zoom_value aspect\"", (char *) NULL);
		return TCL_ERROR;
	}
	/* window to render in */
	tkwin2 = Tk_NameToWindow(interp, argv[1], tkwin);
	if (tkwin2 == NULL) {
		Tcl_AppendResult(interp, "couldn't get window", (char *) NULL);
		return TCL_ERROR;
	}

        /* set width and height for the window */
        win_width = Tk_Width(tkwin2);
        win_height = Tk_Height(tkwin2);

	/* mapfile to read */
	fn = argv[2];

	/* center latitude */
	if (Tcl_GetInt(interp, argv[3], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lat??", (char *) NULL);
		return TCL_ERROR;
	}
	centerlatdeg = foo;

	if (Tcl_GetInt(interp, argv[4], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lat??", (char *) NULL);
		return TCL_ERROR;
	}
	centerlatmin = foo;

	/* center longitude */
	if (Tcl_GetInt(interp, argv[5], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lon??", (char *) NULL);
		return TCL_ERROR;
	}
	centerlondeg = foo;
	if (Tcl_GetInt(interp, argv[6], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lon??", (char *) NULL);
		return TCL_ERROR;
	}
	centerlonmin = foo;

	/* zoom value */
	if (Tcl_GetInt(interp, argv[7], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad zoomval", (char *) NULL);
		return TCL_ERROR;
	}
	zoomval = foo;

	/* aspect value */
	if (Tcl_GetInt(interp, argv[8], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad aspect?", (char *) NULL);
		return TCL_ERROR;
	}
	aspect = (double)foo;

        /* set up the graphics contexts for drawing different colors */
	for (i=0; i < 8; i++) {
		colorPtr = Tk_GetColor(interp, tkwin2, (Colormap) None,
			Tk_GetUid(CTable[i]));
		if (colorPtr == NULL) {
			Tcl_AppendResult(interp, "no color??", (char *) NULL);
			return TCL_ERROR;
		}
		values.foreground = colorPtr->pixel;
		gc[i] = Tk_GetGC(tkwin2, GCForeground, &values);
        }

        if (mapfileread == FALSE) {
 		if (dbnamebuffer[0] == '\0') {
    			strcpy(dbnamebuffer,getenv("USER"));
			if (Debugging) 
			  fprintf(debug_port,"database is %s\n", dbnamebuffer);
			PQsetdb(dbnamebuffer);
			tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", 
					       dbnamebuffer,
					       TCL_GLOBAL_ONLY);
		}
		if (*fn != '@') {
		  (void) PQexec("begin");
		  if ((filein = p_open(argv[2], INV_READ)) < 0) {
		    Tcl_AppendResult(interp,
				     "Unable to open map database file", 
				     (char *) NULL);
		    (void) PQexec("end");
		    return TCL_ERROR;
		  }
		  mapfilepoints = (int) p_read(filein, p, PCount*PNTSiz);
		  p_close(filein);
		  (void) PQexec("end");
		}
		else { /* read from a unix file */
		  if ((filein = open((fn+1), O_RDONLY)) < 0) {
		    Tcl_AppendResult(interp,
				     "Unable to open map database file", 
				     (char *) NULL);
		    return TCL_ERROR;
		  }
		  mapfilepoints = (int) read(filein, p, PCount*PNTSiz);
		  close(filein);
		}
		mapfileread = TRUE;
	}

	Tk_MakeWindowExist(tkwin2);

	/* clear the map display window */
	XClearWindow(Tk_Display(tkwin2), Tk_WindowId(tkwin2));
	attmask = CWBackingStore;
	atts.backing_store = Always;
	Tk_ChangeWindowAttributes(tkwin2,attmask,&atts);

        /* set up the drawing parameters */
	Latitude  = (centerlatdeg * 6000)+(centerlatmin * 100); /* center points for disp */
	Longitude = (centerlondeg * 6000)+(centerlonmin * 100);
	zoom      =(double)zoomval;
        winw = win_width / 2;
        winh = win_height / 2;
	X_Scale   = zoom * aspect / (10800.00 / (float)winw) ;	/* e.g. 33.75 = 10800 / 320  for 640x400*/
	Y_Scale   = zoom * 100.0 / (5400.00/ (float)winh);	/*      27.00 =  5400 / 200  for 640x400*/
	ymax = QSPAN/zoom;
	ymin = -ymax;
	xmax = HSPAN / (zoom * (aspect / 100.0));  /* was (zoom * 1.2); */
	xmin = -xmax;
	xspan = xmax - xmin;
	yspan = ymax - ymin;


	for (pp = p,pend = p+mapfilepoints/6; pp < pend; pp++) { /* do displacement	*/
		x = ((int)pp->Lon)*100 - Longitude;
		y = ((int)pp->Lat)*100 - Latitude;
				/* wrap around for East-West	*/
		if (x < -HSPAN)
			x += WSPAN;
		else if (x > HSPAN)
			x -= WSPAN;
				/* ignore pts outside magnified area	*/
		if((x < xmin || x > xmax || y < ymin || y > ymax)) {
                        if ((int)pp->Code > 5)
                        	gcindex =  colr[(int)pp->Code / 1000];
			newseg = 1;
			continue;
		}
		if (abs(x - LonPrv) >= xspan || abs(y - LatPrv) >= yspan)
			newseg = 1;
		LonPrv = x;
		LatPrv = y;
				/* scale pts w/in area of a 640x400 window */
		x = winw + (x * X_Scale)/10000;
		y = winh - (y * Y_Scale)/10000;
				/* ignore duplicates	*/
		if (x == xprv && y == yprv)
			continue;
				/* if over edge, set for return	*/
               	if (newseg || (int)pp->Code > 5 || abs(z - x) > (winw - 5)) {
                        if ((int)pp->Code > 5)
                        	gcindex =  colr[(int)pp->Code / 1000];
			XDrawPoint(Tk_Display(tkwin2), Tk_WindowId(tkwin2),
				gc[gcindex], x, y);
			newseg = 0;
		}
		else XDrawLine(Tk_Display(tkwin2), Tk_WindowId(tkwin2),
	  		  gc[gcindex], xprv, yprv, x, y);
		xprv = x;
		yprv = y;
		z = x;
	      }
	return TCL_OK;
}



/************** set current lat and lon from mouse click *******************/
int
MouseLatLonCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
        int winw, winh;
	int foo;
	Tk_Window tkwin2;
	int mousex, mousey, x, y;
        int latval, lonval;
        int latdeg, latmin;
        int londeg, lonmin;
        char buff[20];

	if (argc < 4) {
		Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			" window_name mouseX mouseY \"", (char *) NULL);
		return TCL_ERROR;
	}
	/* window to render in */
	tkwin2 = Tk_NameToWindow(interp, argv[1], tkwin);
	if (tkwin2 == NULL) {
		Tcl_AppendResult(interp, "couldn't get window", (char *) NULL);
		return TCL_ERROR;
	}

	/* mouse X */
	if (Tcl_GetInt(interp, argv[2], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad mouse x??", (char *) NULL);
		return TCL_ERROR;
	}
	mousex = foo;

	/* mouse Y */
	if (Tcl_GetInt(interp, argv[3], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lon??", (char *) NULL);
		return TCL_ERROR;
	}
	mousey = foo;

        winw = Tk_Width(tkwin2) / 2;
        winh = Tk_Height(tkwin2) /2;

        lonval = Longitude + ((10000 * (mousex - winw)) / X_Scale);
        latval = Latitude - ((10000 * (mousey - winh)) / Y_Scale);

			/* wrap around for East-West	*/
	if (lonval < -HSPAN)
		lonval += WSPAN;
	else if (lonval > HSPAN)
		lonval -= WSPAN;

        londeg = lonval / 6000;
        lonmin = abs((lonval % 6000)/100);

        latdeg = latval / 6000;
        latmin = abs((latval % 6000)/100);


        /* set globals with latdeg latmin londeg lonmin for use  */

        if (londeg >= 180) {
	  londeg = 180;
          lonmin = 0;
        }

        if (londeg <= -180) {
	  londeg = -180;
          lonmin = 0;
        }


        if (latdeg >= 90) {
	  latdeg = 90;
          latmin = 0;
        }

        if (latdeg <= -90) {
	  latdeg = -90;
          latmin = 0;
        }

        sprintf(buff, "%d", londeg);
        Tcl_SetVar(interp, "MOUSE_LON_DEG", buff, TCL_GLOBAL_ONLY);

        sprintf(buff, "%d", lonmin);
        Tcl_SetVar(interp, "MOUSE_LON_MIN", buff, TCL_GLOBAL_ONLY);

        sprintf(buff, "%d", latdeg);
        Tcl_SetVar(interp, "MOUSE_LAT_DEG", buff, TCL_GLOBAL_ONLY);

        sprintf(buff, "%d", latmin);
        Tcl_SetVar(interp, "MOUSE_LAT_MIN", buff, TCL_GLOBAL_ONLY);


	return TCL_OK;
}

/************** set current lat and lon from mouse click *******************/
int
GetMapLatLonCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
        int winw, winh;
	int foo;
	Tk_Window tkwin2;
	int mousex, mousey, x, y;
        int latval, lonval;
        int latdeg, latmin;
        int londeg, lonmin;
        char buff[20];

	if (argc < 4) {
		Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			" window_name X Y \"", (char *) NULL);
		return TCL_ERROR;
	}
	/* window to render in */
	tkwin2 = Tk_NameToWindow(interp, argv[1], tkwin);
	if (tkwin2 == NULL) {
		Tcl_AppendResult(interp, "couldn't get window", (char *) NULL);
		return TCL_ERROR;
	}

	/* mouse X */
	if (Tcl_GetInt(interp, argv[2], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad mouse x??", (char *) NULL);
		return TCL_ERROR;
	}
	mousex = foo;

	/* mouse Y */
	if (Tcl_GetInt(interp, argv[3], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lon??", (char *) NULL);
		return TCL_ERROR;
	}
	mousey = foo;

        winw = Tk_Width(tkwin2) / 2;
        winh = Tk_Height(tkwin2) /2;

        lonval = Longitude + ((10000 * (mousex - winw)) / X_Scale);
        latval = Latitude - ((10000 * (mousey - winh)) / Y_Scale);

			/* wrap around for East-West	*/
	if (lonval < -HSPAN)
		lonval += WSPAN;
	else if (lonval > HSPAN)
		lonval -= WSPAN;

        londeg = lonval / 6000;
        lonmin = abs((lonval % 6000)/100);

        latdeg = latval / 6000;
        latmin = abs((latval % 6000)/100);


        /* set globals with latdeg latmin londeg lonmin for use  */


        if (londeg >= 180) {
	  londeg = 180;
          lonmin = 0;
        }

        if (londeg <= -180) {
	  londeg = -180;
          lonmin = 0;
        }


        if (latdeg >= 90) {
	  latdeg = 90;
          latmin = 0;
        }

        if (latdeg <= -90) {
	  latdeg = -90;
          latmin = 0;
        }

        sprintf(buff, "%d", latdeg);
	Tcl_AppendElement(interp, buff, 0);
        sprintf(buff, "%d", latmin);
	Tcl_AppendElement(interp, buff, 0);

        sprintf(buff, "%d", londeg);
	Tcl_AppendElement(interp, buff, 0);
        sprintf(buff, "%d", lonmin);
	Tcl_AppendElement(interp, buff, 0);

	return TCL_OK;
}

/**************** window x and y from lat and long *************/
int
GetMapXYCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
      	int inlatdeg, inlondeg, inlatmin, inlonmin, zoomval;
        int lonval, latval, win_width, win_height, winw, winh;
        double aspect;
	int i, foo;
	Tk_Window tkwin2;
	int x, y;
	char buff[50];

	if (argc < 6) {
		Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			" window_name latitude_degrees latitude_minutes ",
                        "longitude_degrees longitude_minutes", (char *) NULL);
		return TCL_ERROR;
	}

	/* window to render in */
	tkwin2 = Tk_NameToWindow(interp, argv[1], tkwin);
	if (tkwin2 == NULL) {
		Tcl_AppendResult(interp, "couldn't get window", (char *) NULL);
		return TCL_ERROR;
	}


        /* set width and height for the window */
        win_width = Tk_Width(tkwin2);
        win_height = Tk_Height(tkwin2);

	/* in latitude */
	if (Tcl_GetInt(interp, argv[2], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lat??", (char *) NULL);
		return TCL_ERROR;
	}
	inlatdeg = foo;

	if (Tcl_GetInt(interp, argv[3], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lat??", (char *) NULL);
		return TCL_ERROR;
	}
	inlatmin = foo;

	/* in longitude */
	if (Tcl_GetInt(interp, argv[4], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lon??", (char *) NULL);
		return TCL_ERROR;
	}
	inlondeg = foo;
	if (Tcl_GetInt(interp, argv[5], &foo) != TCL_OK) {
		Tcl_AppendResult(interp, "bad lon??", (char *) NULL);
		return TCL_ERROR;
	}
	inlonmin = foo;

        /* set up the drawing parameters */
	latval = (inlatdeg * 6000)+(inlatmin * 100); /* in points for disp */
	lonval = (inlondeg * 6000)+(inlonmin * 100);
        winw = win_width / 2;
        winh = win_height / 2;
	x = lonval - Longitude;
	y = latval - Latitude;
	/* wrap around for East-West	*/
	if (x < -HSPAN)
		x += WSPAN;
	else if (x > HSPAN)
		x -= WSPAN;


	/* ignore pts outside magnified area	*/
	if((x < xmin || x > xmax || y < ymin || y > ymax)) {
	  sprintf(buff,"-1 -1");
	  Tcl_AppendResult(interp, buff, (char *) NULL);
	  return TCL_OK;
	  }
	/* scale pts w/in area of a 640x400 window */
	x = winw + (x * X_Scale)/10000;
	y = winh - (y * Y_Scale)/10000;


	sprintf(buff,"%d %d",x,y);
	Tcl_AppendResult(interp, buff, (char *) NULL);


	return TCL_OK;
}


/************** get a bounding box for the current map *******************/
int
MapBoxCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
  Tk_Window tkwin2;
  int x, y;
  char buff1[50];
  char buff2[50];

  if (argc < 2) {
		Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			" window_name ", (char *) NULL);
		return TCL_ERROR;
	      }

  /* window to render in */
  tkwin2 = Tk_NameToWindow(interp, argv[1], tkwin);
  if (tkwin2 == NULL) {
    Tcl_AppendResult(interp, "couldn't get window", (char *) NULL);
    return TCL_ERROR;
  }

  Tcl_VarEval(interp, "getmaplatlon ",argv[1], " 0 0", (char *)NULL);

  strcpy (buff1, interp->result);
  
  Tcl_VarEval(interp, "getmaplatlon ", argv[1], " [winfo width ", argv[1], 
	      "] [winfo height ", argv[1], "]", (char *)NULL);

  strcpy (buff2, interp->result);
 
  Tcl_ResetResult(interp);
  Tcl_AppendElement(interp,buff1, 0);
  Tcl_AppendElement(interp,buff2, 0);

  return TCL_OK;


}
