[comp.sources.misc] v05i072: CGA/EGA Perspective Map Program

sampson@killer.DALLAS.TX.US (Steve Sampson) (12/07/88)

Posting-number: Volume 5, Issue 72
Submitted-by: "Steve Sampson" <sampson@killer.DALLAS.TX.US>
Archive-name: mapper

This is for Turbo-C users, no binaries included.  Easily modified for
other compilers.


#! /bin/sh
# Contents:  mapper.doc mapper.c
# Wrapped by sampson on Mon Dec 05 15:07:18 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f mapper.doc -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mapper.doc\"
else
echo shar: Extracting \"mapper.doc\" \(4532 characters\)
sed "s/^X//" >mapper.doc <<'END_OF_mapper.doc'
X
X                             The Mapper Program
X
X                                 Version 1.7
X
X                              22 November 1988
X
X
X         I was interested in techniques  for producing maps, and  found
X      the article by William D. Johnston in the May and June 1979  Byte
X      Magazine.    This   two  part  article   provided  an   excellent
X      introduction and source  code in Basic  Language.   His code  was
X      restricted to the algorithms and  did not get involved with  user
X      interface.  To evaluate his algorithms and try out the displays I
X      coded the program and a simple interface in Turbo-C Version  1.5.
X      The program in its current form is highly based on Mr. Johnston's
X      algorithms and provides no significant additional capabilities.
X
X      I also found  a high resolution database  called the Micro  World
X      Data Bank II (MWDBII).   This database was  1 megabyte in  length
X      and good down  to minutes  of a degree.   See the  C source  code
X      comments for availability.
X
X      To run the  program and receive  help you  use the DOS  common
X      method of the question option "/?".  Just type "mapper/?" and the
X      following usage help will be displayed:
X
X              Usage: mapper [/bcdgilmrsx]
X
X                /b   Boundaries Off
X                /c   Countries On
X                /dn  Database ('MWDBII' Default)
X                /g   Grid lines On
X                /i   Islands Off
X                /l   Lakes Off
X                /mn  Map Resolution (5 Default)
X                /r   Rivers On
X                /s   States On
X                /x   Colors On
X
X              Defaults to Boundaries and Islands On
X
X      The defaults are what I thought should be fairly common.  The map
X      database has  5 resolutions,  and can  be selected  with the  'm'
X      option.  5 is the  lowest resolution and 1  is the greatest.   If
X      you have several different databases  you can use the 'd'  option
X      and provide the path and filename (128 Characters max).  The  'm'
X      and 'd' options  should be placed at  the end.   They can be  put
X      anywhere  but  it's  a  little  easier  at  the  end.    Example:
X      mapper/glrsm1.  If you use the option in the middle you will need
X      to put a  space between it  and the  remaining options.  Example:
X      mapper/glddata /rs.  These are the most foolproof methods.  Note:
X      The level 5 database included doesn't really use the options yet.
X      The program works as advertised on level 1. There are some errors
X      with the database as you'll see.  I've converted the database  to
X      ASCII, and am working on cleaning up the errors and redundancies.
X
X         A little about the speed of the result.  The program is  quite
X      slow on an 8088 without a math coprocessor, and speed is  getting
X      acceptable on  an 80286.   The  C language  standard uses  double
X      precision math.    This is  a  waste  with the  current  database
X      resolution.  An integer version  of the math routines would  sure
X      speed up  the program  quite  a bit.    The mapper  program  uses
X      Turbo-C auto detect  of a  math coprocessor  and graphics  device
X      type (CGA, EGA, and VGA).
X
X         If you want to quit the  plotting on the screen, just hit  any
X      key and the bell will  sound and exit you back  to DOS.  You  can
X      also use Control-C to get out.
X
X         The C program  lists three sources  for the  Micro World  Data
X      Bank II database.  The  database is 1 Megabyte (800K  Compressed)
X      and is just too much data for reasonable downloading.   To see if
X      the program  would  be  useful  for you  I  included  a  level  5
X      resolution map (the lowest resolution).  This particular database
X      has all  the data  thrown together  so the  command line  options
X      aren't fully functional.  They  become functional at about  level
X      3 I believe.
X
X         This program was tested  on a PC XT  Clone, 640K, and NSI  EGA
X      board.  Also a Zenith Z-248 with 512k and CGA was tested.   Other
X      configurations will need to be tested.
X
X      Due to  the  grid  method used,  it  shouldn't  be used  with  an
X      Azimuthal Equidistant map.   You can  try it once  to see what  I
X      mean.  There's lots of room for improvements, "Handle It!".
X
X      Thanks to Mr. Johnston for his article and algorithms.
X
X      USMail: Steve R. Sampson, Box 45668, Tinker AFB, Oklahoma, 73145
X      Compuserve: 75136,626  Unix: sampson@killer.dallas.tx
END_OF_mapper.doc
if test 4532 -ne `wc -c <mapper.doc`; then
    echo shar: \"mapper.doc\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mapper.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mapper.c\"
else
echo shar: Extracting \"mapper.c\" \(13949 characters\)
sed "s/^X//" >mapper.c <<'END_OF_mapper.c'
X/*
X *	mapper.c
X *
X *	Version 1.7 by Steven R. Sampson, November 1988
X *
X *	Based on a program and article by William D. Johnston
X *	Copyright (c) May-June 1979 BYTE, All Rights Reserved
X *
X *	This program draws three types of map projections:
X *	Perspective, Modified Perspective, and Azimuthal Equidistant.
X *
X *	Compiled with Turbo-C V1.5
X */
X
X#include <dos.h>
X#include <math.h>
X#include <stdio.h>
X#include <conio.h>
X#include <string.h>
X#include <stdlib.h>
X#include <graphics.h>
X
Xtypedef int	bool;
X
X/* Program Constants */
X
X#define	FALSE	(bool) 0
X#define	TRUE	(bool) ~FALSE
X
X#define	PI	(3.141593F)
X#define	HALFPI	(1.570796F)
X#define	TWOPI	(2.0F * PI)		/* Two Pi alias 360 Degrees	     */
X
X#define	RADIAN	(180.0F / PI )		/* One radian			     */
X#define	TWO	(2.0F / RADIAN)		/* 2 degrees in radians		     */
X#define	TEN	(10.0F / RADIAN)	/* 10 degrees in radians	     */
X#define	EIGHTY	(80.0F / RADIAN)	/* 80 degrees in radians	     */
X#define	EARTH	(6378.0F)		/* Mean radius of earth (Kilometers) */
X
X/* Program Globals */
X
XFILE	*fp;
X
Xfloat	angle, maxplot, center_lat, center_lon, lat, lon, distance,
X	sin_of_distance, cos_of_distance, sin_of_center_lat, cos_of_center_lat,
X	scale, g, h2, facing_azimuth, aspect;
X
Xint	option, center_x, center_y, grid_color, level = 5;
Xint	GraphDriver = DETECT, GraphMode;
X
Xchar	optstring[] = "bcd:gilm:rsx?";
Xchar	database[128] = "mwdbii";	/* default name 'MWDBII'	     */
X					/* leave room for pathname!	     */
Xbool	boundaries = TRUE,		/* defaults to Boundaries, Islands   */
X	countries  = FALSE,
X	grids      = FALSE,
X	islands    = TRUE,
X	lakes      = FALSE,
X	rivers     = FALSE,
X	states     = FALSE,
X	colors     = FALSE;
X
X/* Forward Declarations, Prototypes */
X
Xextern	int	getopt(int, char **, char *);
Xextern	int	optind, opterr;
Xextern	char	*optarg;
X
Xfloat	parse(char *);
Xvoid	grid(void), plotmap(void), prompts(void), quit(void);
Xbool	compute(float *, float *, int *, int *);
X
X
Xmain(argc, argv)
Xint	argc;
Xchar	*argv[];
X{
X	register int	i;
X	int		err, xasp, yasp;
X
X	registerbgidriver(EGAVGA_driver);
X	registerbgidriver(CGA_driver);
X
X	setcbrk(TRUE);		/* Allow Control-C checking		     */
X	ctrlbrk(quit);		/* Execute quit() if Control-C detected	     */
X
X	while ((i = getopt(argc, argv, optstring)) != -1)  {
X		switch (i)  {
X		case 'b':
X			boundaries = FALSE;
X			break;
X		case 'c':
X			countries = TRUE;
X			break;
X		case 'd':
X			strcpy(database, optarg);
X			break;
X		case 'g':
X			grids = TRUE;
X			break;
X		case 'i':
X			islands = FALSE;
X			break;
X		case 'l':
X			lakes = TRUE;
X			break;
X		case 'm':
X			level = atoi(optarg);
X			break;
X		case 'r':
X			rivers = TRUE;
X			break;
X		case 's':
X			states = TRUE;
X			break;
X		case 'x':
X			colors = FALSE;
X			break;
X		case '?':
X		default:
X		      printf("Usage: mapper [/bcdgilmrsx]\n\n");
X		      printf("  /b   Boundaries Off\n");
X		      printf("  /c   Countries On\n");
X		      printf("  /dn  Database ('MWDBII' Default)\n");
X		      printf("  /g   Grid lines On\n");
X		      printf("  /i   Islands Off\n");
X		      printf("  /l   Lakes On\n");
X		      printf("  /mn  Map Resolution (5 Default)\n");
X		      printf("  /r   Rivers On\n");
X		      printf("  /s   States On\n");
X		      printf("  /x   Colors On\n\n");
X		      printf("Defaults to Boundaries and Islands On\n");
X		      exit(0);
X		}
X	}
X
X	if ((fp = fopen(database, "rb")) == (FILE *)NULL)  {
X		printf("\007Error: Can't locate Database '%s'\n", database);
X		exit(1);
X	}
X
X	initgraph(&GraphDriver, &GraphMode, "");/* initialize graphics	     */
X	err = graphresult();
X
X	restorecrtmode();			/* get back to text mode     */
X
X	if (err < 0)  {
X		printf("Graphics Error - %s\n", grapherrormsg(err));
X		exit(-1);
X	}
X
X	center_x = getmaxx() / 2;		/* get screen size for x, y  */
X	center_y = getmaxy() / 2;
X	getaspectratio(&xasp, &yasp);		/* squish factor for y axis  */
X	aspect = (float)xasp / (float)yasp;
X
X	prompts();				/* get the basic map info    */
X	setgraphmode(GraphMode);		/*  and go to graphics mode  */
X
X	if (GraphMode != CGAHI)  {
X		setbkcolor(BLACK);		/* must be EGA or VGA then   */
X		if (colors)
X			grid_color = EGA_LIGHTRED;
X		else
X			grid_color = EGA_LIGHTGRAY;
X	} else
X		grid_color = LIGHTGRAY;		/* CGA only has two colors   */
X
X	setcolor(LIGHTGRAY);
X
X	/*
X	 *	See if data plotting is even needed
X	 */
X
X	if (boundaries || countries || islands || lakes || rivers || states)
X		plotmap();			/* display map on screen     */
X
X	if (grids)
X		grid();				/* draw lat & long ref lines */
X
X	if (print)
X		printscreen();			/* relay screen to printer   */
X
X	sound(800);				/* 800 Hz for 1/4 a second   */
X	delay(125);
X	nosound();
X
X	getch();				/* pause until key pressed   */
X	closegraph();				/* graphics off		     */
X	fclose(fp);				/* close database file	     */
X
X	exit(0);
X}
X
X/*
X *	Return to operator following Control-C
X */
X
Xvoid quit()
X{
X	closegraph();
X	fclose(fp);
X
X	exit(0);
X}
X
X/*
X *	Operator prompts and input.
X */
X
Xvoid prompts()
X{
X	char	temp[16];
X	float	ret, altitude;
X
X	printf("West Longitudes and South Lattitudes are negative\n");
X
X	/* input the world Lat & Long that is to be centered on */
X	/*   then convert the human notation to radians         */
X
X	do  {
X		printf("\nLatitude of the map center [+-]dd.mm : ");
X		scanf("%s", temp);
X		ret = parse(temp);
X	} while (ret > 90.0F || ret < -90.0F);
X
X	/* the arcosine function has trouble at 90 degrees */
X
X	if (ret == 90.0F)
X		ret = 89.9F;
X
X	if (ret == -90.0F)
X		ret = -89.9F;
X
X	center_lat = ret / RADIAN;
X	sin_of_center_lat = sin(center_lat);
X	cos_of_center_lat = cos(center_lat);
X
X	do  {
X		printf("Longitude of the map center [+-]ddd.mm : ");
X		scanf("%s", temp);
X		ret = parse(temp);
X	} while (ret > 180.0F || ret < -180.0F);
X
X	center_lon = ret / RADIAN;
X
X	do  {
X		printf("\nSelect from the following options:\n\n");
X		printf("  1 - Perspective Projection\n");
X		printf("  2 - Modified Perspective Projection\n");
X		printf("  3 - Azimuthal Equidistant Projection\n\n");
X		printf("Choice : ");
X		scanf("%d", &option);
X	} while (option < 1 || option > 3);
X
X	if (option == 3)  {
X		maxplot = PI;		/* use HALFPI for less area	    */
X		scale = (float)center_y / maxplot;
X		return;
X	}
X
X	/* input the height above the terrain */
X
X	printf("\nObserver altitude (km) : ");
X	scanf("%f", &altitude);
X
X	h2 = EARTH + altitude;
X	maxplot = acos(EARTH / h2);
X
X	/* the operator can orient the world upside down if they want */
X
X	do  {
X		printf("Observer facing azimuth (0 - 359 degrees) : ");
X		scanf("%f", &facing_azimuth);
X	} while (facing_azimuth < 0.0F || facing_azimuth > 360.0F);
X
X	facing_azimuth = -facing_azimuth / RADIAN;
X
X	/* calculate the scale for the polar coordinates */
X
X	scale = (float)center_y / (EARTH * sin(maxplot));
X
X	/* for the perspective projection */
X
X	g = EARTH * (h2 - EARTH * cos(maxplot));
X}
X
X
X/*
X *	Convert the database to the desired projection by computation.
X *
X *	This code is a hand translation from BASIC to C based on Mr. Johnstons
X *	code.  It is a non-mathematicians idea of what he meant.
X *
X *	Return TRUE if offscale else FALSE.
X */
X
Xbool compute(lat, lon, x, y)
Xregister float	*lat, *lon;
Xregister int	*x, *y;
X{
X	float	sin_of_lat,
X		cos_of_lat,
X		abs_delta_lon,			/* absolute value	     */
X		delta_lon,			/* x distance from center    */
X		delta_lat,			/* y distance from center    */
X		temp;				/* temporary storage	     */
X
X	/* normalize the longitude to +/- PI */
X
X	delta_lon = *lon - center_lon;
X
X	if (delta_lon < -PI)
X		delta_lon = delta_lon + TWOPI;
X
X	if (delta_lon > PI)
X		delta_lon = delta_lon - TWOPI;
X
X	abs_delta_lon = fabs(delta_lon);
X
X	/*
X	 *	If the delta_lon is within .00015 radians of 0 then
X	 *	the difference is considered exactly zero.
X	 *
X	 *	This also simplifys the great circle bearing calculation.
X	 */
X
X	if (abs_delta_lon <= 0.00015F)  {
X		delta_lat = fabs(center_lat - *lat);
X
X		if (delta_lat > maxplot)
X			return TRUE;		/* offscale		     */
X
X		if (*lat < center_lat)
X			angle = PI;
X		else
X			angle = 0.0F;
X
X		sin_of_distance = sin(delta_lat);
X		cos_of_distance = cos(delta_lat);
X	}
X
X	/*
X	 *	Check if delta_lon is within .00015 radians of PI.
X	 */
X
X	else if (fabs(PI - abs_delta_lon) <= 0.00015F)  {
X		delta_lat = PI - center_lat - *lat;
X
X		if (delta_lat > PI)  {
X			delta_lat = TWOPI - delta_lat;
X			angle = PI;
X		} else
X			angle = 0.0F;
X
X		if (delta_lat > maxplot)
X			return TRUE;		/* offscale		     */
X
X		sin_of_distance = sin(delta_lat);
X		cos_of_distance = cos(delta_lat);
X	}
X
X	/*
X	 *	Simple calculations are out, now get cosmic.
X	 */
X
X	else  {
X		sin_of_lat = sin(*lat);
X		cos_of_lat = cos(*lat);
X
X		cos_of_distance = sin_of_center_lat * sin_of_lat +
X				    cos_of_center_lat * cos_of_lat *
X				      cos(delta_lon);
X
X		distance = acos(cos_of_distance);
X
X		if (distance > maxplot)
X			return TRUE;		/* offscale		     */
X
X		sin_of_distance = sin(distance);
X
X		temp = (sin_of_lat - sin_of_center_lat * cos_of_distance) /
X			(cos_of_center_lat * sin_of_distance);
X
X		if (temp < -1.0F || temp > 1.0F)
X			return TRUE;		/* offscale		     */
X
X		angle = acos(temp);
X
X		if (delta_lon < 0.0F)
X			angle = TWOPI - angle;
X	}
X
X	if (facing_azimuth != 0.0F)  {
X		angle = angle - facing_azimuth;
X		if (angle < 0.0F)
X			angle = TWOPI + angle;
X	}
X
X	angle = HALFPI - angle;
X
X	if (angle < -PI)
X		angle = angle + TWOPI;
X
X	switch (option)  {
X	case 1:
X		temp  = (scale * (g * sin_of_distance)) /
X				(h2 - EARTH * cos_of_distance);
X		break;
X	case 2:
X		temp = scale * EARTH * sin_of_distance;
X		break;
X	case 3:
X		temp = scale * distance;
X	}
X
X	/* convert polar to rectangular, correct for screen aspect */
X
X	*x = center_x + (int)(temp * cos(angle));
X	*y = center_y - (int)(temp * sin(angle) * aspect);
X
X	return FALSE;
X}
X
X/*
X *	Read the database and plot points or lines.
X *
X *	The database is Micro World Data Bank II.  It's based on the
X *	CIA WDB-II tape available from NTIS.  Micro WDB-II was created
X *	by Micro Doc.  Placed in the public domain by Fred Pospeschil
X *	and Antonio Riveria.  Check on availability at:
X *	1-402-291-0795  (6-9 PM Central)
X *
X *	Austin Code Works has something called: The World Digitized
X *	that sounds like the same thing ($30.00), 1-512-258-0785
X *
X *	Lone Star Software has something called: The World Digitized
X *	that sounds like the same thing ($6.00), 1-800-445-6172.
X *
X *	Database is in Intel word order:
X *	code_lsb, code_msb, lat_lsb, lat_msb, lon_lsb, lon_msb
X *
X *	Code:	Integer, two meanings:
X *		1.  Detail Level (1 Highest - 5 Lowest)
X *
X *		2.  Header (1xxx - 7xxx)	Command Line Options
X *
X *			1xxx	Boundaries		/b
X *			2xxx	Countries		/c
X *	(decimal)	4xxx	States			/s
X *			5xxx	Islands			/i
X *			6xxx	Lakes			/l
X *			7xxx	Rivers			/r
X *
X *	Lat & Long:  Integer
X *		Representing Minutes of degree
X */
X
Xvoid plotmap()
X{
X	struct	{ short code, lat, lon; } coord;
X	float	lat, lon;
X	int	x, y;
X	bool	point;
X
X	point = TRUE;
X	while (fread(&coord, sizeof coord, 1, fp) > 0)  {
X
X		if (kbhit())  {
X			grids = print = FALSE;
X			getch();
X			return;
X		}
X			
X		/*
X		 *	Skip data that has been optioned out.
X		 */
X
X		if (coord.code < level)
X			continue;
X
X		if (coord.code > 5)  {		/* must be a header	     */
X
X			point = TRUE;
X
X			switch (coord.code / 1000)  {
X			case 1:
X				if (boundaries)  {
X					if (colors)
X						setcolor(EGA_LIGHTGRAY);
X					break;
X				}
X				else
X					continue;
X			case 2:
X				if (countries)  {
X					if (colors)
X						setcolor(EGA_BROWN);
X					break;
X				}
X				else
X					continue;
X			case 4:
X				if (states)  {
X					if (colors)
X						setcolor(EGA_BROWN);
X					break;
X				}
X				else
X					continue;
X			case 5:
X				if (islands)  {
X					if (colors)
X						setcolor(EGA_LIGHTGRAY);
X					break;
X				}
X				else
X					continue;
X			case 6:
X				if (lakes)  {
X					if (colors)
X						setcolor(EGA_BLUE);
X					break;
X				}
X				else
X					continue;
X			case 7:
X				if (rivers)  {
X					if (colors)
X						setcolor(EGA_GREEN);
X					break;
X				}
X				else
X					continue;
X			}
X		}
X
X		/*  Convert database minutes of a degree to radians */
X
X		lat =  (float) coord.lat / 60.0F / RADIAN;
X		lon =  (float) coord.lon / 60.0F / RADIAN;
X
X		if (compute(&lat, &lon, &x, &y))  {
X			point = TRUE;		/* offscale		     */
X			continue;
X		}
X
X		if (point)  {
X			putpixel(x, y, getcolor());/* put down a dot	     */
X			moveto(x, y);
X			point = FALSE;
X		}
X		else
X			lineto(x, y);		/* connect the dots	     */
X	}
X}
X
X/*
X *	parse +-ddd.mm
X *
X *	Change human degrees, and minutes to computer decimal.
X *	Probably designed a monster for a simple solution here...
X */
X
Xfloat parse(string)
Xchar	*string;
X{
X	char	*ptr, degrees[8], minutes[8];
X	float	num;
X
X	strcpy(degrees, "       ");		/* pre-load with blanks      */
X	strcpy(minutes, "       ");
X
X	/* if no decimal point we assume a whole number */
X
X	if ( (ptr = strchr(string, '.')) == (char *)NULL )
X		return atof(string);
X
X	/* else use the decimal point to offset */
X
X	*ptr++ = '\0';
X
X	strcpy(degrees, string);
X	num = atof(degrees);
X
X	switch (strlen(ptr))  {
X	case 0:
X		return atof(string);
X	case 1:
X	case 2:
X		strcpy(minutes, ptr);
X		break;
X	default:
X		return 361.0F;	/* This will produce an error		     */
X	}
X
X	if (num >= 0.0F)
X		num += atof(minutes) / 60.0F;
X	else
X		num -= atof(minutes) / 60.0F;
X
X	return num;
X}
X
X
X/*
X *	Draw grid lines from -180 to +180 Degrees (Longitude Lines),
X *	as well as +80 to -80 Degrees (Lattitude Lines).
X */
X
Xvoid grid()
X{
X	float	lat, lon;
X	int	x, y, pass1;
X
X	setcolor(grid_color);
X
X	for (lon = -PI; lon <= PI; lon += TEN)  {
X		pass1 = TRUE;
X		for (lat = EIGHTY; lat > -EIGHTY; lat -= TEN)  {
X			if (!compute(&lat, &lon, &x, &y))  {
X				if (pass1)  {
X					putpixel(x, y, grid_color);
X					moveto(x, y);
X					pass1 = FALSE;
X				} else
X					lineto(x, y);
X			} else
X				pass1 = TRUE;
X		}
X
X		if (kbhit())  {
X			print = FALSE;
X			getch();
X			return;
X		}
X	}
X
X	for (lat = EIGHTY; lat > -EIGHTY; lat -= TEN)  {
X		pass1 = TRUE;
X		for (lon = -PI; lon <= PI; lon += TEN)  {
X			if (!compute(&lat, &lon, &x, &y))  {
X				if (pass1)  {
X					putpixel(x, y, grid_color);
X					moveto(x, y);
X					pass1 = FALSE;
X				} else
X					lineto(x, y);
X			} else
X				pass1 = TRUE;
X
X		}
X
X		if (kbhit())  {
X			print = FALSE;
X			getch();
X			return;
X		}
X	}
X}
X
X/* EOF */
END_OF_mapper.c
if test 13949 -ne `wc -c <mapper.c`; then
    echo shar: \"mapper.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0