ftp.nice.ch/pub/next/science/cartography/ICAO.0.7b.s.tar.gz#/ICAOfNEXT.0.7b/geometry.c

This is geometry.c in view mode; [Download] [Up]

/****************************************************************
 *                                                              *
 *                        I C A O   M a p  -                    *
 *                                                              *
 *             A n   A v i a t i o n   U t i l i t y            *
 *                                                              *
 *                                                              *
 *            Copyright (C) 1993, 1994  Martin Pauly            *
 *                                                              *
 *                   N e x t   V e r s i o n                    *
 *                                                              *
 *       Copyright (C) 1994  Stefan Leuker & Oliver Meyer       *
 *                                                              *
 *   This file may be freely distributed only if it includes    *
 *                the above copyright notice.                   *
 *                                                              *
 ****************************************************************/

#import "geometry.h"

double unitChange[]={ 0, 6076.115, 1852, 1.852, 1 }; 
char *unitName[]={NULL, "ft", "m", "km", "NM"}; 

double 
distance(LOCATION a, LOCATION b)
{
	struct
	{
		double              x, y, z;
	}                   start, dest;
	double              chord;
	double              angle;

	/* transfer to Cartesian system */

	start.x = EARTH_RADIUS * cos(internal2rad(a.longitude))
		* cos(internal2rad(a.latitude));
	start.y = EARTH_RADIUS * sin(internal2rad(a.longitude))
		* cos(internal2rad(a.latitude));
	start.z = EARTH_RADIUS * sin(internal2rad(a.latitude));

	dest.x = EARTH_RADIUS * cos(internal2rad(b.longitude))
		* cos(internal2rad(b.latitude));
	dest.y = EARTH_RADIUS * sin(internal2rad(b.longitude))
		* cos(internal2rad(b.latitude));
	dest.z = EARTH_RADIUS * sin(internal2rad(b.latitude));

	/* length of the chord between start and destination */

	chord = sqrt((start.x - dest.x) * (start.x - dest.x)
							 + (start.y - dest.y) * (start.y - dest.y)
							 + (start.z - dest.z) * (start.z - dest.z));

	angle = acos(1 - (chord * chord) / (2 * EARTH_RADIUS * EARTH_RADIUS));

	return EARTH_RADIUS * angle / 1.852;	/* result in NM */
}

double 
distance2(LOCATION a, LOCATION b)
{
	double              lat1, lat2, long1, long2, dl;
	double              alpha = 1 / 297.0;
	double              s, s0, sdiva, stemp, cosquad;
	double              ae = 6378388;

	/*
	 * L. Kiefer: Die Beruecksichtigung der Ellipsoidgestalt der Erde in der
	 * Flugnavigation 
	 */

	lat1 = internal2rad(a.latitude);
	long1 = internal2rad(a.longitude);
	lat2 = internal2rad(b.latitude);
	long2 = internal2rad(b.longitude);
	dl = internal2rad(a.longitude - b.longitude);

	if ((lat1 == lat2) && (long1 == long2))
		return 0;

	if (lat1 == lat2)
		lat1 += 0.000000001;

	if (long1 == long2)
		long1 += 0.000000001;

	cosquad = cos((lat2 - lat1) / 2);
	cosquad *= cosquad;

	stemp = (2 * cosquad - 1) * cos(dl / 2) * cos(dl / 2) - (2 * cosquad - 1) * sin(dl / 2) * sin(dl / 2);


/* this made problems, I suspect the pow function of my library:
  stemp = (2*pow(cos((lat2-lat1)/2),2)-1)*pow(cos(dl/2),2) -
    (2*pow(cos((lat2+lat1)/2),2)-1)*pow(sin(dl/2),2);
*/

	s0 = acos(stemp);

/* again, we should avoid use of the power function 
  sdiva = s0 - alpha *
    (pow(sin((lat2+lat1)/2),2)*pow(cos((lat2-lat1)/2),2)*(s0-3*sin(s0))/(1+cos(s0)) +
     pow(cos((lat2+lat1)/2),2)*pow(sin((lat2-lat1)/2),2)*(s0+3*sin(s0))/(1-cos(s0)));
*/

	sdiva = s0 - alpha *
		(sin((lat2 + lat1) / 2) * sin((lat2 + lat1) / 2) * cos((lat2 - lat1) / 2) *
		 cos((lat2 - lat1) / 2) * (s0 - 3 * sin(s0)) / (1 + cos(s0)) +
	cos((lat2 + lat1) / 2) * cos((lat2 + lat1) / 2) * sin((lat2 - lat1) / 2) *
		 sin((lat2 - lat1) / 2) * (s0 + 3 * sin(s0)) / (1 - cos(s0)));

	s = sdiva * ae;

	return s / 1852;
}


double 
truetrack(LOCATION a, LOCATION b)
{
	double              tt = 0;
	long                dlat, dlong;
	int                 offset, factor;
	LOCATION            p1, p2, p3;

	p1 = a;
	p2.latitude = b.latitude;
	p2.longitude = a.longitude;
	p3 = b;

	dlat = b.latitude - a.latitude;
	dlong = b.longitude - a.longitude;


	if (dlat == 0)
	{
		if (dlong > 0)
			tt = 90;
		if (dlong < 0)
			tt = 270;
	}
	else
	{
		offset = 0;
		factor = 1;

		if ((dlat < 0) && (dlong > 0))
		{
			offset = 90;
			factor = -1;
		}
		if ((dlat < 0) && (dlong < 0))
			offset = 180;
		if ((dlat > 0) && (dlong < 0))
		{
			offset = 270;
			factor = -1;
		}

		dlat *= (dlat >= 0) ? 1 : -1;
		dlong *= (dlong >= 0) ? 1 : -1;

/*      tt = factor * (180 * atan((double) dlong / (double) dlat) / PI);  */
		tt = factor * (180 * atan(distance(p2, p3) / distance(p1, p2)) / PI);

		if (tt < 0)
			tt += 90;
		tt += offset;
	}

	return tt;										/* not implemented either... */
}


double 
truetrack1(LOCATION a, LOCATION b)
{
	double              tt;
	double              dlat;
	double              d;

	d = distance(a, b);
	dlat = (b.latitude - a.latitude) / 360000;

	if (d)
	{
		tt = 180 * acos(dlat * ONE_DEGREE / d) / PI;
	}
	else
		tt = 0;											/* default value if a==b */

	return tt;
}




/* convert a string 99.99'99"d into the internal degrees representation */
/* bug fixed Jan-16-94: format 999.99'99"d also accepted */

long 
deg2num(char *string)
{
	int                 deg, min, sec;
	long                result=0; /* to avoid Compiler Warning */

	if (string[2] == '.')
	{
		deg = atoi(string);
		min = atoi(string + 3);
		sec = atoi(string + 6);

		result = (((deg * 60) + min) * 60 + sec) * 100;

		if ((string[9] == 'W') || (string[9] == 'S'))
			result *= -1;
	}
	else
	if (string[3] == '.')
	{
		deg = atoi(string);
		min = atoi(string + 4);
		sec = atoi(string + 7);

		result = (((deg * 60) + min) * 60 + sec) * 100;

		if ((string[10] == 'W') || (string[10] == 'S'))
			result *= -1;
	}

	return result;
}



/* convert internal representation to human readable string */

char               *
internal2string(LOCATION pos)
{
	static char         build[25];

	sprintf(build, "%2d.%2d\047%2d\042%c %2d.%2d\047%2d\042%c",
					(int)(abs(pos.latitude) / 360000),
					(int)((abs(pos.latitude) / 6000) % 60),
					(int)((abs(pos.latitude) / 100) % 60),
					(pos.latitude < 0) ? 'S' : 'N',
					(int)(abs(pos.longitude) / 360000),
					(int)((abs(pos.longitude) / 6000) % 60),
					(int)((abs(pos.longitude) / 100) % 60),
					(pos.longitude < 0) ? 'W' : 'E');

	if (build[0] == ' ')
		build[0] = '0';
	if (build[3] == ' ')
		build[3] = '0';
	if (build[6] == ' ')
		build[6] = '0';
	if (build[11] == ' ')
		build[11] = '0';
	if (build[14] == ' ')
		build[14] = '0';
	if (build[15] == ' ')
		build[15] = '0';						/* also handle 3-digit longitude */
	if (build[17] == ' ')
		build[17] = '0';
	if (build[18] == ' ')
		build[18] = '0';

	return build;
}


/* convert degrees into radians */

double 
rad(double deg)
{
	return (deg * PI / 180);
}


/* convert radians into degrees */

double 
deg(double rad)
{
	return (rad * 180 / PI);
}


/* convert internal repr. into radians */

double 
internal2rad(long internal)
{
	return (((double)internal) * PI / 64800000);
}


/* convert radians into internal rep. */

long 
rad2internal(double rad)
{
	return (long)(rad * 64800000 / PI);
}





/*
   conformal conical projection with two standard parallels (Lambert)
  
   The following two routines require a geographical loaction,
   given in geographical coordinates (radian).
   They return a coordinate pair (x,y) for the position on a two-dimensional
   map on which the given location should be displayed.
   -1 < x < 1;  -1 < y < 1.
   The zero-meridian (the one going through London) will be the line from
   (0,0) to (0, -1).
   If anything goes wrong with these two functions: the mathematical background
   can be found in Josef Hoschek: "Mathematische Grundlagen der Kartographie".
  
   note: the latitudes of the two standard parallels are stored in global vars
   v1 and v2. It doesn't matter which one is which.
*/

double 
proj_x_lambert(double latitude, double longitude)
{
	double              x;

	x = 2 * (sqrt(cos(v1) * cos(v1) + (sin(v1) - sin(latitude))
								* (sin(v1) + sin(v2))) / (sin(v1) + sin(v2)))
		* sin((sin(v1) + sin(v2)) * longitude / 2);

	return x;
}


double 
proj_y_lambert(double latitude, double longitude)
{
	double              y;

	y = -2 * (sqrt(cos(v1) * cos(v1) + (sin(v1) - sin(latitude))
								 * (sin(v1) + sin(v2))) / (sin(v1) + sin(v2)))
		* cos((sin(v1) + sin(v2)) * longitude / 2);

	return y;
}



double 
proj_x_mercator(double latitude, double longitude)
{
	return longitude * mercatorscale;
}



double 
proj_y_mercator(double latitude, double longitude)
{
	return log(tan(PI / 4 + latitude / 2)) * mercatorscale;	/* log is natural log */
}





double 
proj_x(double latitude, double longitude)
{
	switch (map_projection)
	{
		case PROJECT_LAMBERT:
		return proj_x_lambert(latitude, longitude);
		break;
	case PROJECT_MERCATOR:
		return proj_x_mercator(latitude, longitude);
		break;
	}
	return 0.0;
}

double 
proj_y(double latitude, double longitude)
{
	switch (map_projection)
	{
		case PROJECT_LAMBERT:
		return proj_y_lambert(latitude, longitude);
		break;
	case PROJECT_MERCATOR:
		return proj_y_mercator(latitude, longitude);
		break;
	}
	return 0.0;
}





/*
   Now, we also need the other way around, i.e. from a given map coordinate
   we'd like to find out what the "real world" coordinate is. The code for
   the reverse lambert projection looks a bit ugly - it was created 
   automatically by Maple...
*/

void 
plane2sphere_mercator(double x, double y, double *latitude, double *longitude)
{
	*latitude = 2 * atan(exp(y / mercatorscale)) - PI / 2;
	*longitude = x / mercatorscale;
}


void 
plane2sphere_lambert(double x, double y, double *latitude, double *longitude)
{
	*latitude = -asin((-4.0 * pow(cos(v1), 2.0) - 4.0 * pow(sin(v1), 2.0) - 4.0 * sin(v1) * sin(v2)
										 + y * y * pow(sin(v1), 2.0) + pow(sin(v1), 2.0) * x * x + 2.0 * y * y * sin(v1) * sin(v2) + 2.0 * sin(v1)
										 * sin(v2) * x * x + y * y * pow(sin(v2), 2.0) + pow(sin(v2), 2.0) * x * x) / (sin(v1) + sin(v2)) / 4);

	*longitude = -2.0 * atan(x / y) / (sin(v1) + sin(v2));
}


void 
plane2sphere(double x, double y, double *latitude, double *longitude)
{
	switch (map_projection)
	{
		case PROJECT_LAMBERT:
		plane2sphere_lambert(x, y, latitude, longitude);
		break;
	case PROJECT_MERCATOR:
		plane2sphere_mercator(x, y, latitude, longitude);
		break;
	}
}


/* convert position x,y (in cm) to human readable string */

char               *
cm2string(double mapx, double mapy)
{
	double              x, y;
	double              latitude, longitude;
	LOCATION            temp;

	x = mapx / scalefactor + originx;
	y = mapy / scalefactor + originy;

	plane2sphere(x, y, &latitude, &longitude);

	temp.latitude = rad2internal(latitude);
	temp.longitude = rad2internal(longitude) + map_zeromeridian;
	return internal2string(temp);
}


/* convert position x,y (in cm) to internal format */

LOCATION 
cm2internal(double mapx, double mapy)
{
	double              x, y;
	double              latitude, longitude;
	LOCATION            temp;

	x = mapx / scalefactor + originx;
	y = mapy / scalefactor + originy;

	plane2sphere(x, y, &latitude, &longitude);

	temp.latitude = rad2internal(latitude);
	temp.longitude = rad2internal(longitude) + map_zeromeridian;
	return temp;
}


/* convert internal format to map position in cm */

void 
internal2cm(LOCATION position, double *mapx, double *mapy)
{
	double              x, y;


	x = proj_x(internal2rad(position.latitude),
						 internal2rad(position.longitude - map_zeromeridian));
	y = proj_y(internal2rad(position.latitude),
						 internal2rad(position.longitude - map_zeromeridian));

	*mapx = (x - originx) * scalefactor;
	*mapy = (y - originy) * scalefactor;
}

/*
  drawing to the map, it may be necessary to find out what offset has to added
  to a true track at a given position to properly draw that angle on the map
  (shifts by zeromeridian etc.):
*/

int 
truenorthoffset(LOCATION pos)
{
	if (map_projection == PROJECT_LAMBERT)
		return (int)((map_zeromeridian - pos.longitude) / 360000);
	else
		return 0;										/* naturally, true north is always up in
																 * Mercator style maps */
}



/*
  geometry primitives
*/


/* test for counter-clock wise movement from p0 via p1 to p2 */
/* from: Sedgewick: Algorithms */

int 
ccw(POINT p0, POINT p1, POINT p2)
{
	int                 dx1, dx2, dy1, dy2, result = -1;

	dx1 = p1.x - p0.x;
	dy1 = p1.y - p0.y;
	dx2 = p2.x - p0.x;
	dy2 = p2.y - p0.y;

	if (dx1 * dy2 > dy1 * dx2)
		result = 1;
	if (dx1 * dy2 < dy1 * dx2)
		result = -1;
	if (dx1 * dy2 == dy1 * dx2)
	{
		if ((dx1 * dx2 < 0) || (dy1 * dy2 < 0))
			result = -1;
		else
		{
			if (dx1 * dx1 + dy1 * dy1 >= dx2 * dx2 + dy2 * dy2)
				result = 0;
			else
				result = 1;
		}
	}
	return result;
}


/* test for intersection of two lines */
/* from: Sedgewick: Algorithms */

int 
intersect(LINE l1, LINE l2)
{
	return ((ccw(l1.p1, l1.p2, l2.p1) * ccw(l1.p1, l1.p2, l2.p2)) <= 0) &&
	((ccw(l2.p1, l2.p2, l1.p1) * ccw(l2.p1, l2.p2, l1.p2)) <= 0);
}



/* test if point t is contained in polygon p */
/* points in p[1]..p[numpoints], p[0] and p[numpoints+1] must be available */
/* from: Sedgewick: Algorithms */

int 
ppcontains(POINT t, POINT * p, int numpoints)
{
	int                 count, i, j;
	LINE                lt, lp;
	POINT               comp;

	/* first make p[1] the point with lowest x among points with lowest y */

	i = 1;
	comp = p[1];
	for (j = 2; j <= numpoints; j++)	/* search */
		if ((p[j].y < comp.y) || ((p[j].y == comp.y) && (p[j].x < comp.x)))
		{
			i = j;
			comp = p[j];
		}

	while (i > 1)									/* reorganie */
	{
		comp = p[1];
		for (j = 1; j < numpoints; j++)
			p[j] = p[j + 1];
		p[numpoints] = comp;
		i--;
	}

	count = 0;
	j = 0;
	p[0] = p[numpoints];
	p[numpoints + 1] = p[1];

	lt.p1 = lt.p2 = t;
	lt.p2.x += 9998;							/* make a very long line */
	lt.p2.y += 9991;

	for (i = 1; i <= numpoints; i++)
	{
		lp.p1 = p[i];
		lp.p2 = p[i];
		if (!intersect(lp, lt))
		{
			lp.p2 = p[j];
			j = i;
			if (intersect(lp, lt))
				count++;
		}
	}

	return count % 2;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.