/* $Id: plmap.c,v 1.2 1994/08/25 04:04:49 mjl Exp $
 * $Log: plmap.c,v $
 * Revision 1.2  1994/08/25  04:04:49  mjl
 * Eliminated unnecessary variable decls.
 *
 * Revision 1.1  1994/07/29  20:26:10  mjl
 * Function to plot map backgrounds, read from the specified map data file.
 * Contributed by Wesley Ebisuzaki.
 *
*/

/*	plmap.c

	Continental Outline and Political Boundary Backgrounds

	Some plots need a geographical background such as the global
	surface temperatures or the population density.  The routine
	plmap() will draw one of the following backgrounds: continental
	outlines, political boundaries, the United States, and the United
	States with the continental outlines.  The routine plmeridians()
	will add the latitudes and longitudes to the background.  After
	the background has been drawn, one can use a contour routine or a
	symbol plotter to finish off the plot.

	Copyright 1991,1993,1994  Wesley Ebisuzaki

	This program can be freely distributed, sale except for media
	costs is prohibited.
*/

#include "plplotP.h"

/*----------------------------------------------------------------------*\
 * void plmap(void (*mapform)(PLINT, PLFLT *, PLFLT *), char *type,
 *            PLFLT minlong, PLFLT maxlong, PLFLT minlat, PLFLT maxlat);
 *
 * plot continental outline in world coordinates
 *
 * v1.4: machine independant version
 * v1.3: replaced plcontinent by plmap, added plmeridians
 * v1.2: 2 arguments:  mapform, type of plot
 * v1.1: change buffersize for amiga - faster reads
 *
 * mapform(PLINT n, PLFLT *x, PLFLT *y) is a routine to transform the
 * coordinate longitudes and latitudes to a plot coordinate system.  By
 * using this transform, we can change from a longitude, latitude
 * coordinate to a polar stereographic project, for example.  Initially,
 * x[0]..[n-1] are the longitudes and y[0]..y[n-1] are the corresponding
 * latitudes.  After the call to mapform(), x[] and y[] should be replaced
 * by the corresponding plot coordinates.  If no transform is desired,
 * mapform can be replaced by NULL.
 * 
 * type is a character string. The value of this parameter determines the
 * type of background. The possible values are,
 *
 * 	"globe"		continental outlines
 * 	"usa"		USA and state boundaries
 * 	"cglobe"	continental outlines and countries
 * 	"usaglobe"	USA, state boundaries and continental outlines
 * 
 * minlong, maxlong are the values of the longitude on the left and right
 * side of the plot, respectively. The value of minlong must be less than
 * the values of maxlong, and the values of maxlong-minlong must be less
 * or equal to 360.
 * 
 * minlat, maxlat are the minimum and maximum latitudes to be plotted on
 * the background.  One can always use -90.0 and 90.0 as the boundary
 * outside the plot window will be automatically eliminated.  However, the
 * program will be faster if one can reduce the size of the background
 * plotted.
\*----------------------------------------------------------------------*/

#define MAP_FILE ".map"
#define OFFSET (180*100)
#define SCALE 100.0
#define W_BUFSIZ	(32*1024)

void
plmap(void (*mapform)(PLINT, PLFLT *, PLFLT *), char *type,
      PLFLT minlong, PLFLT maxlong, PLFLT minlat, PLFLT maxlat)
{
    PLINT wrap, sign;
    int i, j;
    PLFLT bufx[200], bufy[200], x[2], y[2];
    short int test[400];
    register FILE *in;
    char filename[100];

    unsigned char n_buff[2], buff[800];
    int n;
    long int t;

#ifdef AMIGA
    int error;
    char *io_buffer;
#endif	

    /*
     * read map outline 
     */
    strcpy(filename,type);
    strcat(filename,MAP_FILE);

    if ((in = plLibOpen(filename)) == NULL)
	return;

#ifdef AMIGA
    /* increase the size of the IO buffer = faster reads */
    /* ANSI C -- however haven't found it necessary on UNIX */
    io_buffer = (char *) malloc(W_BUFSIZ);
    if (io_buffer == NULL) {
	fprintf(stderr, "ran out of memory: plmap\n");
	return;
    }
    error = setvbuf(in,io_buffer,_IOFBF,W_BUFSIZ);
    if (error != 0) {
	fprintf(stderr, "plmap: setvbuf err %d\n",error);
	free(io_buffer);
	return;
    }
#endif

    for (;;) {
	/* read in # points in segment */
	if (fread(n_buff, sizeof (unsigned char), 2, in) == 0) break;
	n = (n_buff[0] << 8) + n_buff[1];
	if (n == 0) break;

	fread(buff, sizeof (unsigned char), 4*n, in);
	if (n == 1) continue;

	for (j = i = 0; i < n; i++, j += 2) {
	    t = (buff[j] << 8) + buff[j+1];
	    bufx[i] = (t - OFFSET) / SCALE;
	}
	for (i = 0; i < n; i++, j += 2) {
	    t = (buff[j] << 8) + buff[j+1];
	    bufy[i] = (t - OFFSET) / SCALE;
	}

	for (i = 0; i < n; i++) {
	    while (bufx[i] < minlong) {
		bufx[i] += 360.0;
	    }
	    while (bufx[i] > maxlong) {
		bufx[i] -= 360.0;
	    }
	}

	/* remove last 2 points if both outside of domain and won't plot */

	while (n > 1) {
	    if ((bufx[n-1] < minlong && bufx[n-2] < minlong) ||
		(bufx[n-1] > maxlong && bufx[n-2] > maxlong) ||
		(bufy[n-1] < minlat && bufy[n-2] < minlat) ||
		(bufy[n-1] > maxlat && bufy[n-2] > maxlat)) {
		n--;
	    }
	    else {
		break;
	    }
	}
	if (n <= 1) continue;

	wrap = 0;
	/* check for wrap around problems */
	for (i = 0; i < n-1; i++) {
	    test[i] = (abs(bufx[i]-bufx[i+1]) > 30.0);
	    if (test[i]) wrap = 1;
	}

	if (wrap == 0) {	
	    if (mapform != NULL) (*mapform)(n,bufx,bufy);
	    plline(n,bufx,bufy);
	}
	else {
	    for (i = 0; i < n-1; i++) {
		x[0] = bufx[i];
		x[1] = bufx[i+1];
		y[0] = bufy[i];
		y[1] = bufy[i+1];
		if (test[i] == 0) {
		    if (mapform != NULL) (*mapform)(2,x,y);
		    plline(2,x,y);
		}
		else {
		    /* segment goes off the edge */
		    sign = (x[1] > x[0]) ? 1 : -1;
		    x[1] -= sign * 360.0;
		    if (mapform != NULL) (*mapform)(2,x,y);
		    plline(2,x,y);
		    x[0] = bufx[i];
		    x[1] = bufx[i+1];
		    y[0] = bufy[i];
		    y[1] = bufy[i+1];
		    x[0] += sign * 360.0;
		    if (mapform != NULL) (*mapform)(2,x,y);
		    plline(2,x,y);
		}
	    }
	}
    }
#ifdef AMIGA
    free(io_buffer);
#endif
}

/*----------------------------------------------------------------------*\
 * void plmeridians(void (*mapform)(PLINT, PLFLT *, PLFLT *), 
 *		    PLFLT dlong, PLFLT dlat, PLFLT minlong, PLFLT maxlong, 
 *		    PLFLT minlat, PLFLT maxlat);
 *
 * Plot the latitudes and longitudes on the background.  The lines 
 * are plotted in the current color and line style.
 * 
 * mapform(PLINT n, PLFLT *x, PLFLT *y) is a routine to transform the
 * coordinate longitudes and latitudes to a plot coordinate system.  By
 * using this transform, we can change from a longitude, latitude
 * coordinate to a polar stereographic project, for example.  Initially,
 * x[0]..x[n-1] are the longitudes and y[0]..y[n-1] are the corresponding
 * latitudes.  After the call to mapform(), x[] and y[] should be replaced
 * by the corresponding plot coordinates.  If no transform is desired,
 * mapform can be replaced by NULL.
 * 
 * dlat, dlong are the interval in degrees that the latitude and longitude
 * lines are to be plotted. 
 * 
 * minlong, maxlong are the values of the longitude on the left and right
 * side of the plot, respectively. The value of minlong must be less than
 * the values of maxlong, and the values of maxlong-minlong must be less
 * or equal to 360.
 * 
 * minlat, maxlat are the minimum and maximum latitudes to be plotted on
 * the background.  One can always use -90.0 and 90.0 as the boundary
 * outside the plot window will be automatically eliminated.  However, the
 * program will be faster if one can reduce the size of the background
 * plotted.
\*----------------------------------------------------------------------*/

#define NSEG 100

void 
plmeridians(void (*mapform)(PLINT, PLFLT *, PLFLT *), 
	    PLFLT dlong, PLFLT dlat,
	    PLFLT minlong, PLFLT maxlong, PLFLT minlat, PLFLT maxlat)
{
    PLFLT yy, xx, temp, x[2], y[2], dx, dy;

    if (minlong > maxlong) {
	temp = minlong;
	minlong = maxlong;
	maxlong = temp;
    }
    if (minlat > maxlat) {
	temp = minlat;
	minlat = maxlat;
	maxlat = temp;
    }
    dx = (maxlong - minlong) / NSEG;
    dy = (maxlat - minlat) / NSEG;

    /* latitudes */

    for (yy = dlat * ceil(minlat/dlat); yy <= maxlat; yy += dlat) {
	if (mapform == NULL) {
	    y[0] = y[1] = yy;
	    x[0] = minlong;
	    x[1] = maxlong;
	    plline(2,x,y);
	}
	else {
	    for (xx = minlong; xx < maxlong; xx += dx) {
	        y[0] = y[1] = yy;
		x[0] = xx;
		x[1] = xx + dx;
	 	(*mapform)(2,x,y);
		plline(2,x,y);
	    }
	}
    }

    /* longitudes */
 
    for (xx = dlong * ceil(minlong/dlong); xx <= maxlong; xx += dlong) {
        if (mapform == NULL) {
            x[0] = x[1] = xx;
            y[0] = minlat;
            y[1] = maxlat;
            plline(2,x,y);
        }
        else {
            for (yy = minlat; yy < maxlat; yy += dy) {
                x[0] = x[1] = xx;
                y[0] = yy;
                y[1] = yy + dy;
                (*mapform)(2,x,y);
                plline(2,x,y);
            }
        }
    }
}
