aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFrank Warmerdam <warmerdam@pobox.com>2000-07-06 23:32:27 +0000
committerFrank Warmerdam <warmerdam@pobox.com>2000-07-06 23:32:27 +0000
commitc5487d590fd83070fa595e0c93ab57e0acc09489 (patch)
tree3bb8bb64da4ed5f84169355f15490ba9c310063a /src
parente786437af7935bbfc6519e25826ac647a1602c74 (diff)
downloadPROJ-c5487d590fd83070fa595e0c93ab57e0acc09489.tar.gz
PROJ-c5487d590fd83070fa595e0c93ab57e0acc09489.zip
New
git-svn-id: http://svn.osgeo.org/metacrs/proj/trunk@848 4e78687f-474d-0410-85f9-8d5e500ac6b2
Diffstat (limited to 'src')
-rw-r--r--src/cs2cs.c408
-rw-r--r--src/geocent.c319
-rw-r--r--src/geocent.h163
-rw-r--r--src/pj_apply_gridshift.c270
-rw-r--r--src/pj_datum_set.c132
-rw-r--r--src/pj_datums.c57
-rw-r--r--src/pj_latlong.c61
-rw-r--r--src/pj_transform.c459
8 files changed, 1869 insertions, 0 deletions
diff --git a/src/cs2cs.c b/src/cs2cs.c
new file mode 100644
index 00000000..4c1005c8
--- /dev/null
+++ b/src/cs2cs.c
@@ -0,0 +1,408 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: PROJ.4
+ * Purpose: Mainline program sort of like ``proj'' for converting between
+ * two coordinate systems.
+ * Author: Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log$
+ * Revision 1.1 2000/07/06 23:32:27 warmerda
+ * New
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+#include "projects.h"
+#include "emess.h"
+
+#define MAX_LINE 200
+#define MAX_PARGS 100
+
+static PJ *fromProj, *toProj;
+
+static int
+reversein = 0, /* != 0 reverse input arguments */
+reverseout = 0, /* != 0 reverse output arguments */
+echoin = 0, /* echo input data to output line */
+tag = '#'; /* beginning of line tag character */
+ static char
+*oform = (char *)0, /* output format for x-y or decimal degrees */
+*oterr = "*\t*", /* output line for unprojectable input */
+*usage =
+"%s\nusage: %s [ -beEfiIlorstvwW [args] ] [ +opts[=arg] ]\n"
+" [+to [+opts[=arg] [ files ]\n";
+
+static struct FACTORS facs;
+static double (*informat)(const char *,
+ char **); /* input data deformatter function */
+
+
+/************************************************************************/
+/* process() */
+/* */
+/* File processing function. */
+/************************************************************************/
+static void process(FILE *fid)
+
+{
+ char line[MAX_LINE+3], *s, pline[40];
+ projUV data;
+
+ for (;;) {
+ double z;
+
+ ++emess_dat.File_line;
+ if (!(s = fgets(line, MAX_LINE, fid)))
+ break;
+ if (!strchr(s, '\n')) { /* overlong line */
+ int c;
+ (void)strcat(s, "\n");
+ /* gobble up to newline */
+ while ((c = fgetc(fid)) != EOF && c != '\n') ;
+ }
+ if (*s == tag) {
+ fputs(line, stdout);
+ continue;
+ }
+
+ if (reversein) {
+ data.v = (*informat)(s, &s);
+ data.u = (*informat)(s, &s);
+ } else {
+ data.u = (*informat)(s, &s);
+ data.v = (*informat)(s, &s);
+ }
+
+ z = strtod( s, &s );
+
+ if (data.v == HUGE_VAL)
+ data.u = HUGE_VAL;
+
+ if (!*s && (s > line)) --s; /* assumed we gobbled \n */
+
+ if ( echoin) {
+ int t;
+ t = *s;
+ *s = '\0';
+ (void)fputs(line, stdout);
+ *s = t;
+ putchar('\t');
+ }
+
+ if (data.u != HUGE_VAL) {
+ if( pj_transform( fromProj, toProj, 1, 0,
+ &(data.u), &(data.v), &z ) != 0 )
+ {
+ data.u = HUGE_VAL;
+ data.v = HUGE_VAL;
+ }
+ }
+
+ if (data.u == HUGE_VAL) /* error output */
+ fputs(oterr, stdout);
+
+ else if (toProj->is_latlong && !oform) { /*ascii DMS output */
+ if (reverseout) {
+ fputs(rtodms(pline, data.v, 'N', 'S'), stdout);
+ putchar('\t');
+ fputs(rtodms(pline, data.u, 'E', 'W'), stdout);
+ } else {
+ fputs(rtodms(pline, data.u, 'E', 'W'), stdout);
+ putchar('\t');
+ fputs(rtodms(pline, data.v, 'N', 'S'), stdout);
+ }
+
+ } else { /* x-y or decimal degree ascii output */
+ if ( toProj->is_latlong ) {
+ data.v *= RAD_TO_DEG;
+ data.u *= RAD_TO_DEG;
+ }
+ if (reverseout) {
+ printf(oform,data.v); putchar('\t');
+ printf(oform,data.u);
+ } else {
+ printf(oform,data.u); putchar('\t');
+ printf(oform,data.v);
+ }
+ }
+
+ putchar(' ');
+ printf( "%.3f", z );
+ fputs("\n", stdout );
+ }
+}
+
+/************************************************************************/
+/* main() */
+/************************************************************************/
+
+int main(int argc, char **argv)
+{
+ char *arg, **eargv = argv, *from_argv[MAX_PARGS], *to_argv[MAX_PARGS],
+ **iargv = argv;
+ FILE *fid;
+ int from_argc=0, to_argc=0, iargc = argc, eargc = 0, c, mon = 0;
+ int have_to_flag = 0, inverse = 0, i;
+
+ if (emess_dat.Prog_name = strrchr(*argv,DIR_CHAR))
+ ++emess_dat.Prog_name;
+ else emess_dat.Prog_name = *argv;
+ inverse = ! strncmp(emess_dat.Prog_name, "inv", 3);
+ if (argc <= 1 ) {
+ (void)fprintf(stderr, usage, pj_release, emess_dat.Prog_name);
+ exit (0);
+ }
+ /* process run line arguments */
+ while (--argc > 0) { /* collect run line arguments */
+ if(**++argv == '-') for(arg = *argv;;) {
+ switch(*++arg) {
+ case '\0': /* position of "stdin" */
+ if (arg[-1] == '-') eargv[eargc++] = "-";
+ break;
+ case 'v': /* monitor dump of initialization */
+ mon = 1;
+ continue;
+ case 'I': /* alt. method to spec inverse */
+ inverse = 1;
+ continue;
+ case 'E': /* echo ascii input to ascii output */
+ echoin = 1;
+ continue;
+ case 't': /* set col. one char */
+ if (arg[1]) tag = *++arg;
+ else emess(1,"missing -t col. 1 tag");
+ continue;
+ case 'l': /* list projections, ellipses or units */
+ if (!arg[1] || arg[1] == 'p' || arg[1] == 'P') {
+ /* list projections */
+ struct PJ_LIST *lp;
+ int do_long = arg[1] == 'P', c;
+ char *str;
+
+ for (lp = pj_list ; lp->id ; ++lp) {
+ (void)printf("%s : ", lp->id);
+ if (do_long) /* possibly multiline description */
+ (void)puts(*lp->descr);
+ else { /* first line, only */
+ str = *lp->descr;
+ while ((c = *str++) && c != '\n')
+ putchar(c);
+ putchar('\n');
+ }
+ }
+ } else if (arg[1] == '=') { /* list projection 'descr' */
+ struct PJ_LIST *lp;
+
+ arg += 2;
+ for (lp = pj_list ; lp->id ; ++lp)
+ if (!strcmp(lp->id, arg)) {
+ (void)printf("%9s : %s\n", lp->id, *lp->descr);
+ break;
+ }
+ } else if (arg[1] == 'e') { /* list ellipses */
+ struct PJ_ELLPS *le;
+
+ for (le = pj_ellps; le->id ; ++le)
+ (void)printf("%9s %-16s %-16s %s\n",
+ le->id, le->major, le->ell, le->name);
+ } else if (arg[1] == 'u') { /* list units */
+ struct PJ_UNITS *lu;
+
+ for (lu = pj_units; lu->id ; ++lu)
+ (void)printf("%12s %-20s %s\n",
+ lu->id, lu->to_meter, lu->name);
+ } else if (arg[1] == 'd') { /* list datums */
+ struct PJ_DATUMS *ld;
+
+ printf("__datum_id__ __ellipse___ __definition/comments______________________________\n" );
+ for (ld = pj_datums; ld->id ; ++ld)
+ {
+ printf("%12s %-12s %-30s\n",
+ ld->id, ld->ellipse_id, ld->defn);
+ if( ld->comments != NULL && strlen(ld->comments) > 0 )
+ printf( "%25s %s\n", " ", ld->comments );
+ }
+ } else
+ emess(1,"invalid list option: l%c",arg[1]);
+ exit(0);
+ continue; /* artificial */
+ case 'e': /* error line alternative */
+ if (--argc <= 0)
+ noargument:
+ emess(1,"missing argument for -%c",*arg);
+ oterr = *++argv;
+ continue;
+ case 'W': /* specify seconds precision */
+ case 'w': /* -W for constant field width */
+ if ((c = arg[1]) != 0 && isdigit(c)) {
+ set_rtodms(c - '0', *arg == 'W');
+ ++arg;
+ } else
+ emess(1,"-W argument missing or non-digit");
+ continue;
+ case 'f': /* alternate output format degrees or xy */
+ if (--argc <= 0) goto noargument;
+ oform = *++argv;
+ continue;
+ case 'r': /* reverse input */
+ reversein = 1;
+ continue;
+ case 's': /* reverse output */
+ reverseout = 1;
+ continue;
+ default:
+ emess(1, "invalid option: -%c",*arg);
+ break;
+ }
+ break;
+
+ } else if (strcmp(*argv,"+to") == 0 ) {
+ have_to_flag = 1;
+
+ } else if (**argv == '+') { /* + argument */
+ if( have_to_flag )
+ {
+ if( to_argc < MAX_PARGS )
+ to_argv[to_argc++] = *argv + 1;
+ else
+ emess(1,"overflowed + argument table");
+ }
+ else
+ {
+ if (from_argc < MAX_PARGS)
+ from_argv[from_argc++] = *argv + 1;
+ else
+ emess(1,"overflowed + argument table");
+ }
+ } else /* assumed to be input file name(s) */
+ eargv[eargc++] = *argv;
+ }
+ if (eargc == 0 ) /* if no specific files force sysin */
+ eargv[eargc++] = "-";
+
+ /*
+ * If no to projection is provided, generate a latlong equivelent to
+ * the from projection.
+ */
+ if( to_argc == 0 )
+ {
+ to_argv[to_argc++] = "proj=latlong";
+ for( i = 0; i < from_argc; i++ )
+ {
+ if( strncmp(from_argv[i],"a=",2) == 0
+ || strncmp(from_argv[i],"b=",2) == 0
+ || strncmp(from_argv[i],"ra=",3) == 0
+ || strncmp(from_argv[i],"ellps=",6) == 0
+ || strncmp(from_argv[i],"datum=",6) == 0 )
+ to_argv[to_argc++] = from_argv[i];
+ }
+ }
+
+ /*
+ * If the user has requested inverse, then just reverse the
+ * coordinate systems.
+ */
+ if( inverse )
+ {
+ int argcount;
+
+ for( i = 0; i < MAX_PARGS; i++ )
+ {
+ char *arg;
+
+ arg = from_argv[i];
+ from_argv[i] = to_argv[i];
+ to_argv[i] = arg;
+ }
+
+ argcount = from_argc;
+ from_argc = to_argc;
+ to_argc = argcount;
+ }
+
+ if (!(fromProj = pj_init(from_argc, from_argv)))
+ {
+ printf( "Using from definition: " );
+ for( i = 0; i < from_argc; i++ )
+ printf( "%s ", from_argv[i] );
+ printf( "\n" );
+
+ emess(3,"projection initialization failure\ncause: %s",
+ pj_strerrno(pj_errno));
+ }
+
+ if (!(toProj = pj_init(to_argc, to_argv)))
+ {
+ printf( "Using to definition: " );
+ for( i = 0; i < to_argc; i++ )
+ printf( "%s ", to_argv[i] );
+ printf( "\n" );
+
+ emess(3,"projection initialization failure\ncause: %s",
+ pj_strerrno(pj_errno));
+ }
+
+ if (mon) {
+ printf( "%c ---- From Coordinate System ----\n", tag );
+ pj_pr_list(fromProj);
+ printf( "%c ---- To Coordinate System ----\n", tag );
+ pj_pr_list(toProj);
+ }
+
+ /* set input formating control */
+ if( !fromProj->is_latlong )
+ informat = strtod;
+ else {
+ informat = dmstor;
+ }
+
+ if( !toProj->is_latlong && !oform )
+ oform = "%.2f";
+
+ /* process input file list */
+ for ( ; eargc-- ; ++eargv) {
+ if (**eargv == '-') {
+ fid = stdin;
+ emess_dat.File_name = "<stdin>";
+
+ } else {
+ if ((fid = fopen(*eargv, "rt")) == NULL) {
+ emess(-2, *eargv, "input file");
+ continue;
+ }
+ emess_dat.File_name = *eargv;
+ }
+ emess_dat.File_line = 0;
+ process(fid);
+ fclose(fid);
+ emess_dat.File_name = 0;
+ }
+ exit(0); /* normal completion */
+}
diff --git a/src/geocent.c b/src/geocent.c
new file mode 100644
index 00000000..e4fbad94
--- /dev/null
+++ b/src/geocent.c
@@ -0,0 +1,319 @@
+/***************************************************************************/
+/* RSC IDENTIFIER: GEOCENTRIC
+ *
+ * ABSTRACT
+ *
+ * This component provides conversions between Geodetic coordinates (latitude,
+ * longitude in radians and height in meters) and Geocentric coordinates
+ * (X, Y, Z) in meters.
+ *
+ * ERROR HANDLING
+ *
+ * This component checks parameters for valid values. If an invalid value
+ * is found, the error code is combined with the current error code using
+ * the bitwise or. This combining allows multiple error codes to be
+ * returned. The possible error codes are:
+ *
+ * GEOCENT_NO_ERROR : No errors occurred in function
+ * GEOCENT_LAT_ERROR : Latitude out of valid range
+ * (-90 to 90 degrees)
+ * GEOCENT_LON_ERROR : Longitude out of valid range
+ * (-180 to 360 degrees)
+ * GEOCENT_A_ERROR : Semi-major axis lessthan or equal to zero
+ * GEOCENT_B_ERROR : Semi-minor axis lessthan or equal to zero
+ * GEOCENT_A_LESS_B_ERROR : Semi-major axis less than semi-minor axis
+ *
+ *
+ * REUSE NOTES
+ *
+ * GEOCENTRIC is intended for reuse by any application that performs
+ * coordinate conversions between geodetic coordinates and geocentric
+ * coordinates.
+ *
+ *
+ * REFERENCES
+ *
+ * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion,
+ * Ralph Toms, February 1996 UCRL-JC-123138.
+ *
+ * Further information on GEOCENTRIC can be found in the Reuse Manual.
+ *
+ * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center
+ * Geospatial Information Division
+ * 7701 Telegraph Road
+ * Alexandria, VA 22310-3864
+ *
+ * LICENSES
+ *
+ * None apply to this component.
+ *
+ * RESTRICTIONS
+ *
+ * GEOCENTRIC has no restrictions.
+ *
+ * ENVIRONMENT
+ *
+ * GEOCENTRIC was tested and certified in the following environments:
+ *
+ * 1. Solaris 2.5 with GCC version 2.8.1
+ * 2. Windows 95 with MS Visual C++ version 6
+ *
+ * MODIFICATIONS
+ *
+ * Date Description
+ * ---- -----------
+ * 25-02-97 Original Code
+ *
+ */
+
+
+/***************************************************************************/
+/*
+ * INCLUDES
+ */
+#include <math.h>
+#include "geocent.h"
+/*
+ * math.h - is needed for calls to sin, cos, tan and sqrt.
+ * geocent.h - is needed for Error codes and prototype error checking.
+ */
+
+
+/***************************************************************************/
+/*
+ * DEFINES
+ */
+#define PI 3.14159265358979323e0
+#define PI_OVER_2 (PI / 2.0e0)
+#define FALSE 0
+#define TRUE 1
+#define COS_67P5 0.38268343236508977 /* cosine of 67.5 degrees */
+#define AD_C 1.0026000 /* Toms region 1 constant */
+
+
+/***************************************************************************/
+/*
+ * GLOBAL DECLARATIONS
+ */
+/* Ellipsoid parameters, default to WGS 84 */
+double Geocent_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
+double Geocent_b = 6356752.3142; /* Semi-minor axis of ellipsoid */
+
+double Geocent_a2 = 40680631590769.0; /* Square of semi-major axis */
+double Geocent_b2 = 40408299984087.05; /* Square of semi-minor axis */
+double Geocent_e2 = 0.0066943799901413800; /* Eccentricity squared */
+double Geocent_ep2 = 0.00673949675658690300; /* 2nd eccentricity squared */
+/*
+ * These state variables are for optimization purposes. The only function
+ * that should modify them is Set_Geocentric_Parameters.
+ */
+
+
+/***************************************************************************/
+/*
+ * FUNCTIONS
+ */
+
+
+long Set_Geocentric_Parameters (double a,
+ double b)
+{ /* BEGIN Set_Geocentric_Parameters */
+/*
+ * The function Set_Geocentric_Parameters receives the ellipsoid parameters
+ * as inputs and sets the corresponding state variables.
+ *
+ * a : Semi-major axis, in meters. (input)
+ * b : Semi-minor axis, in meters. (input)
+ */
+ long Error_Code = GEOCENT_NO_ERROR;
+
+ if (a <= 0.0)
+ Error_Code |= GEOCENT_A_ERROR;
+ if (b <= 0.0)
+ Error_Code |= GEOCENT_B_ERROR;
+ if (a < b)
+ Error_Code |= GEOCENT_A_LESS_B_ERROR;
+ if (!Error_Code)
+ {
+ Geocent_a = a;
+ Geocent_b = b;
+ Geocent_a2 = a * a;
+ Geocent_b2 = b * b;
+ Geocent_e2 = (Geocent_a2 - Geocent_b2) / Geocent_a2;
+ Geocent_ep2 = (Geocent_a2 - Geocent_b2) / Geocent_b2;
+ }
+ return (Error_Code);
+} /* END OF Set_Geocentric_Parameters */
+
+
+void Get_Geocentric_Parameters (double *a,
+ double *b)
+{ /* BEGIN Get_Geocentric_Parameters */
+/*
+ * The function Get_Geocentric_Parameters returns the ellipsoid parameters
+ * to be used in geocentric coordinate conversions.
+ *
+ * a : Semi-major axis, in meters. (output)
+ * b : Semi-minor axis, in meters. (output)
+ */
+
+ *a = Geocent_a;
+ *b = Geocent_b;
+} /* END OF Get_Geocentric_Parameters */
+
+
+long Convert_Geodetic_To_Geocentric (double Latitude,
+ double Longitude,
+ double Height,
+ double *X,
+ double *Y,
+ double *Z)
+{ /* BEGIN Convert_Geodetic_To_Geocentric */
+/*
+ * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates
+ * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
+ * according to the current ellipsoid parameters.
+ *
+ * Latitude : Geodetic latitude in radians (input)
+ * Longitude : Geodetic longitude in radians (input)
+ * Height : Geodetic height, in meters (input)
+ * X : Calculated Geocentric X coordinate, in meters (output)
+ * Y : Calculated Geocentric Y coordinate, in meters (output)
+ * Z : Calculated Geocentric Z coordinate, in meters (output)
+ *
+ */
+ long Error_Code = GEOCENT_NO_ERROR;
+ double Rn; /* Earth radius at location */
+ double Sin_Lat; /* sin(Latitude) */
+ double Sin2_Lat; /* Square of sin(Latitude) */
+ double Cos_Lat; /* cos(Latitude) */
+
+ if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2))
+ { /* Latitude out of range */
+ Error_Code |= GEOCENT_LAT_ERROR;
+ }
+ if ((Longitude < -PI) || (Longitude > (2*PI)))
+ { /* Longitude out of range */
+ Error_Code |= GEOCENT_LON_ERROR;
+ }
+ if (!Error_Code)
+ { /* no errors */
+ if (Longitude > PI)
+ Longitude -= (2*PI);
+ Sin_Lat = sin(Latitude);
+ Cos_Lat = cos(Latitude);
+ Sin2_Lat = Sin_Lat * Sin_Lat;
+ Rn = Geocent_a / (sqrt(1.0e0 - Geocent_e2 * Sin2_Lat));
+ *X = (Rn + Height) * Cos_Lat * cos(Longitude);
+ *Y = (Rn + Height) * Cos_Lat * sin(Longitude);
+ *Z = ((Rn * (1 - Geocent_e2)) + Height) * Sin_Lat;
+
+ }
+ return (Error_Code);
+} /* END OF Convert_Geodetic_To_Geocentric */
+
+void Convert_Geocentric_To_Geodetic (double X,
+ double Y,
+ double Z,
+ double *Latitude,
+ double *Longitude,
+ double *Height)
+{ /* BEGIN Convert_Geocentric_To_Geodetic */
+/*
+ * The function Convert_Geocentric_To_Geodetic converts geocentric
+ * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude,
+ * and height), according to the current ellipsoid parameters.
+ *
+ * X : Geocentric X coordinate, in meters. (input)
+ * Y : Geocentric Y coordinate, in meters. (input)
+ * Z : Geocentric Z coordinate, in meters. (input)
+ * Latitude : Calculated latitude value in radians. (output)
+ * Longitude : Calculated longitude value in radians. (output)
+ * Height : Calculated height value, in meters. (output)
+ *
+ * The method used here is derived from 'An Improved Algorithm for
+ * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
+ */
+
+/* Note: Variable names follow the notation used in Toms, Feb 1996 */
+
+ double W; /* distance from Z axis */
+ double W2; /* square of distance from Z axis */
+ double T0; /* initial estimate of vertical component */
+ double T1; /* corrected estimate of vertical component */
+ double S0; /* initial estimate of horizontal component */
+ double S1; /* corrected estimate of horizontal component */
+ double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */
+ double Sin3_B0; /* cube of sin(B0) */
+ double Cos_B0; /* cos(B0) */
+ double Sin_p1; /* sin(phi1), phi1 is estimated latitude */
+ double Cos_p1; /* cos(phi1) */
+ double Rn; /* Earth radius at location */
+ double Sum; /* numerator of cos(phi1) */
+ int At_Pole; /* indicates location is in polar region */
+
+ At_Pole = FALSE;
+ if (X != 0.0)
+ {
+ *Longitude = atan2(Y,X);
+ }
+ else
+ {
+ if (Y > 0)
+ {
+ *Longitude = PI_OVER_2;
+ }
+ else if (Y < 0)
+ {
+ *Longitude = -PI_OVER_2;
+ }
+ else
+ {
+ At_Pole = TRUE;
+ *Longitude = 0.0;
+ if (Z > 0.0)
+ { /* north pole */
+ *Latitude = PI_OVER_2;
+ }
+ else if (Z < 0.0)
+ { /* south pole */
+ *Latitude = -PI_OVER_2;
+ }
+ else
+ { /* center of earth */
+ *Latitude = PI_OVER_2;
+ *Height = -Geocent_b;
+ return;
+ }
+ }
+ }
+ W2 = X*X + Y*Y;
+ W = sqrt(W2);
+ T0 = Z * AD_C;
+ S0 = sqrt(T0 * T0 + W2);
+ Sin_B0 = T0 / S0;
+ Cos_B0 = W / S0;
+ Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
+ T1 = Z + Geocent_b * Geocent_ep2 * Sin3_B0;
+ Sum = W - Geocent_a * Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0;
+ S1 = sqrt(T1*T1 + Sum * Sum);
+ Sin_p1 = T1 / S1;
+ Cos_p1 = Sum / S1;
+ Rn = Geocent_a / sqrt(1.0 - Geocent_e2 * Sin_p1 * Sin_p1);
+ if (Cos_p1 >= COS_67P5)
+ {
+ *Height = W / Cos_p1 - Rn;
+ }
+ else if (Cos_p1 <= -COS_67P5)
+ {
+ *Height = W / -Cos_p1 - Rn;
+ }
+ else
+ {
+ *Height = Z / Sin_p1 + Rn * (Geocent_e2 - 1.0);
+ }
+ if (At_Pole == FALSE)
+ {
+ *Latitude = atan(Sin_p1 / Cos_p1);
+ }
+} /* END OF Convert_Geocentric_To_Geodetic */
diff --git a/src/geocent.h b/src/geocent.h
new file mode 100644
index 00000000..58fca48e
--- /dev/null
+++ b/src/geocent.h
@@ -0,0 +1,163 @@
+#ifndef GEOCENT_H
+#define GEOCENT_H
+
+/***************************************************************************/
+/* RSC IDENTIFIER: GEOCENTRIC
+ *
+ * ABSTRACT
+ *
+ * This component provides conversions between Geodetic coordinates (latitude,
+ * longitude in radians and height in meters) and Geocentric coordinates
+ * (X, Y, Z) in meters.
+ *
+ * ERROR HANDLING
+ *
+ * This component checks parameters for valid values. If an invalid value
+ * is found, the error code is combined with the current error code using
+ * the bitwise or. This combining allows multiple error codes to be
+ * returned. The possible error codes are:
+ *
+ * GEOCENT_NO_ERROR : No errors occurred in function
+ * GEOCENT_LAT_ERROR : Latitude out of valid range
+ * (-90 to 90 degrees)
+ * GEOCENT_LON_ERROR : Longitude out of valid range
+ * (-180 to 360 degrees)
+ * GEOCENT_A_ERROR : Semi-major axis less than or equal to zero
+ * GEOCENT_B_ERROR : Semi-minor axis less than or equal to zero
+ * GEOCENT_A_LESS_B_ERROR : Semi-major axis less than semi-minor axis
+ *
+ *
+ * REUSE NOTES
+ *
+ * GEOCENTRIC is intended for reuse by any application that performs
+ * coordinate conversions between geodetic coordinates and geocentric
+ * coordinates.
+ *
+ *
+ * REFERENCES
+ *
+ * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion,
+ * Ralph Toms, February 1996 UCRL-JC-123138.
+ *
+ * Further information on GEOCENTRIC can be found in the Reuse Manual.
+ *
+ * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center
+ * Geospatial Information Division
+ * 7701 Telegraph Road
+ * Alexandria, VA 22310-3864
+ *
+ * LICENSES
+ *
+ * None apply to this component.
+ *
+ * RESTRICTIONS
+ *
+ * GEOCENTRIC has no restrictions.
+ *
+ * ENVIRONMENT
+ *
+ * GEOCENTRIC was tested and certified in the following environments:
+ *
+ * 1. Solaris 2.5 with GCC version 2.8.1
+ * 2. Windows 95 with MS Visual C++ version 6
+ *
+ * MODIFICATIONS
+ *
+ * Date Description
+ * ---- -----------
+ *
+ *
+ */
+
+
+/***************************************************************************/
+/*
+ * DEFINES
+ */
+#define GEOCENT_NO_ERROR 0x0000
+#define GEOCENT_LAT_ERROR 0x0001
+#define GEOCENT_LON_ERROR 0x0002
+#define GEOCENT_A_ERROR 0x0004
+#define GEOCENT_B_ERROR 0x0008
+#define GEOCENT_A_LESS_B_ERROR 0x0010
+
+
+/***************************************************************************/
+/*
+ * FUNCTION PROTOTYPES
+ */
+
+/* ensure proper linkage to c++ programs */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+ long Set_Geocentric_Parameters (double a,
+ double b);
+/*
+ * The function Set_Geocentric_Parameters receives the ellipsoid parameters
+ * as inputs and sets the corresponding state variables.
+ *
+ * a : Semi-major axis, in meters. (input)
+ * b : Semi-minor axis, in meters. (input)
+ */
+
+
+ void Get_Geocentric_Parameters (double *a,
+ double *b);
+/*
+ * The function Get_Geocentric_Parameters returns the ellipsoid parameters
+ * to be used in geocentric coordinate conversions.
+ *
+ * a : Semi-major axis, in meters. (output)
+ * b : Semi-minor axis, in meters. (output)
+ */
+
+
+ long Convert_Geodetic_To_Geocentric (double Latitude,
+ double Longitude,
+ double Height,
+ double *X,
+ double *Y,
+ double *Z);
+/*
+ * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates
+ * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
+ * according to the current ellipsoid parameters.
+ *
+ * Latitude : Geodetic latitude in radians (input)
+ * Longitude : Geodetic longitude in radians (input)
+ * Height : Geodetic height, in meters (input)
+ * X : Calculated Geocentric X coordinate, in meters. (output)
+ * Y : Calculated Geocentric Y coordinate, in meters. (output)
+ * Z : Calculated Geocentric Z coordinate, in meters. (output)
+ *
+ */
+
+
+ void Convert_Geocentric_To_Geodetic (double X,
+ double Y,
+ double Z,
+ double *Latitude,
+ double *Longitude,
+ double *Height);
+/*
+ * The function Convert_Geocentric_To_Geodetic converts geocentric
+ * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude,
+ * and height), according to the current ellipsoid parameters.
+ *
+ * X : Geocentric X coordinate, in meters. (input)
+ * Y : Geocentric Y coordinate, in meters. (input)
+ * Z : Geocentric Z coordinate, in meters. (input)
+ * Latitude : Calculated latitude value in radians. (output)
+ * Longitude : Calculated longitude value in radians. (output)
+ * Height : Calculated height value, in meters. (output)
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GEOCENT_H */
diff --git a/src/pj_apply_gridshift.c b/src/pj_apply_gridshift.c
new file mode 100644
index 00000000..0f7e39a6
--- /dev/null
+++ b/src/pj_apply_gridshift.c
@@ -0,0 +1,270 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: PROJ.4
+ * Purpose: Apply datum shifts based on grid shift files (normally NAD27 to
+ * NAD83 or the reverse). This module is responsible for keeping
+ * a list of loaded grids, and calling with each one that is
+ * allowed for a given datum (expressed as the nadgrids= parameter).
+ * Author: Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log$
+ * Revision 1.1 2000/07/06 23:32:27 warmerda
+ * New
+ *
+ */
+
+#define PJ_LIB__
+
+#include <projects.h>
+#include <string.h>
+#include <math.h>
+
+#define GRID_MAX 100
+
+/* used only by pj_get_grid() and pj_deallocate_grids() */
+static int grid_count = 0;
+static char **grid_names = NULL;
+static struct CTABLE **grid_list = NULL;
+
+/* used only by pj_load_nadgrids() and pj_deallocate_grids() */
+static struct CTABLE **last_nadgrids_list = NULL;
+static char *last_nadgrids = NULL;
+
+/************************************************************************/
+/* pj_deallocate_grids() */
+/* */
+/* Deallocate all loaded grids. */
+/************************************************************************/
+
+void pj_deallocate_grids()
+
+{
+ if( grid_count > 0 )
+ {
+ int i;
+
+ for( i = 0; i < grid_count; i++ )
+ {
+ if( grid_list[i] != NULL )
+ nad_free( grid_list[i] );
+ pj_dalloc( grid_names[i] );
+ }
+
+ pj_dalloc( grid_names );
+ pj_dalloc( grid_list );
+
+ grid_names = NULL;
+ grid_list = NULL;
+
+ grid_count = 0;
+ }
+
+ if( last_nadgrids != NULL )
+ {
+ pj_dalloc( last_nadgrids );
+ last_nadgrids = NULL;
+
+ pj_dalloc( last_nadgrids_list );
+ last_nadgrids_list = NULL;
+ }
+}
+
+/************************************************************************/
+/* pj_get_grid() */
+/* */
+/* Find the requested grid in the list, or if not present, try */
+/* and load it. On failure returns NULL and sets pj_errno. */
+/************************************************************************/
+
+static struct CTABLE *pj_get_grid( const char *name )
+
+{
+ int i;
+
+/* -------------------------------------------------------------------- */
+/* First look in the existing list. */
+/* -------------------------------------------------------------------- */
+ for( i = 0; i < grid_count; i++ )
+ {
+ if( strcmp( grid_names[i], name ) == 0 )
+ {
+ if( grid_list[i] == NULL )
+ pj_errno = -38;
+
+ return grid_list[i];
+ }
+ }
+
+/* -------------------------------------------------------------------- */
+/* Add entry for this file in the grid list. */
+/* -------------------------------------------------------------------- */
+ if( grid_count == 0 )
+ {
+ grid_names = pj_malloc(sizeof(char *) * GRID_MAX);
+ memset( grid_names, 0, sizeof(char *) * GRID_MAX );
+ grid_list = pj_malloc(sizeof(struct CTABLE *) * GRID_MAX );
+ memset( grid_list, 0, sizeof(struct CTABLE *) * GRID_MAX );
+ }
+ else if( grid_count >= GRID_MAX )
+ {
+ pj_errno = -38;
+ return NULL;
+ }
+
+ grid_count++;
+
+ grid_names[grid_count-1] = (char *) pj_malloc(strlen(name)+1);
+ strcpy( grid_names[grid_count-1], name );
+
+/* -------------------------------------------------------------------- */
+/* Read the file. */
+/* -------------------------------------------------------------------- */
+ grid_list[grid_count-1] = nad_init( (char *) name );
+
+ return grid_list[grid_count-1];
+}
+
+/************************************************************************/
+/* pj_load_nadgrids() */
+/* */
+/* This functions loads the list of grids corresponding to a */
+/* particular nadgrids string into a list, and returns it. The */
+/* list is kept around till a request is made with a different */
+/* string in order to cut down on the string parsing cost, and */
+/* the cost of building the list of tables each time. */
+/************************************************************************/
+
+static struct CTABLE **pj_load_nadgrids( const char *nadgrids )
+
+{
+ int nadgrids_count = 0;
+ const char *s;
+
+ pj_errno = 0;
+
+ if( last_nadgrids != NULL
+ && strcmp(nadgrids,last_nadgrids) == 0 )
+ return last_nadgrids_list;
+
+/* -------------------------------------------------------------------- */
+/* Free old one, if any, and make space for new list. */
+/* -------------------------------------------------------------------- */
+ if( last_nadgrids != NULL )
+ {
+ pj_dalloc(last_nadgrids);
+ }
+
+ last_nadgrids = (char *) pj_malloc(strlen(nadgrids)+1);
+ strcpy( last_nadgrids, nadgrids );
+
+ if( last_nadgrids_list == NULL )
+ last_nadgrids_list = (struct CTABLE **)
+ pj_malloc(sizeof(struct CTABLE *) * GRID_MAX);
+
+/* -------------------------------------------------------------------- */
+/* Loop processing names out of nadgrids one at a time. */
+/* -------------------------------------------------------------------- */
+ for( s = nadgrids; *s != '\0'; s++ )
+ {
+ int end_char;
+ char name[128];
+
+ for( end_char = 0;
+ s[end_char] != '\0' && s[end_char] != ',';
+ end_char++ ) {}
+
+ if( end_char > sizeof(name) )
+ {
+ pj_errno = -38;
+ return NULL;
+ }
+
+ strncpy( name, s, end_char );
+ name[end_char] = '\0';
+
+ s += end_char;
+ if( *s == ',' )
+ s++;
+
+ last_nadgrids_list[nadgrids_count] = pj_get_grid( name );
+ if( last_nadgrids_list[nadgrids_count] == NULL )
+ return NULL;
+
+ nadgrids_count++;
+ }
+
+ last_nadgrids_list[nadgrids_count] = NULL;
+
+ return last_nadgrids_list;
+}
+
+/************************************************************************/
+/* pj_apply_gridshift() */
+/************************************************************************/
+
+int pj_apply_gridshift( const char *nadgrids, int inverse,
+ long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ struct CTABLE **tables = pj_load_nadgrids( nadgrids );
+ int i;
+
+ if( tables == NULL )
+ return pj_errno;
+
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+ LP input, output;
+ int itable;
+
+ input.phi = y[io];
+ input.lam = x[io];
+
+ /* keep trying till we find a table that works */
+ for( itable = 0; tables[itable] != NULL; itable++ )
+ {
+ output = nad_cvt( input, inverse, tables[itable] );
+ if( output.lam != HUGE_VAL )
+ break;
+ }
+
+ if( output.lam == HUGE_VAL )
+ {
+ pj_errno = -38;
+ return pj_errno;
+ }
+ else
+ {
+ y[io] = output.phi;
+ x[io] = output.lam;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/pj_datum_set.c b/src/pj_datum_set.c
new file mode 100644
index 00000000..417479a5
--- /dev/null
+++ b/src/pj_datum_set.c
@@ -0,0 +1,132 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: PROJ.4
+ * Purpose: Apply datum definition to PJ structure from initialization string.
+ * Author: Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log$
+ * Revision 1.1 2000/07/06 23:32:27 warmerda
+ * New
+ *
+ */
+
+#include <projects.h>
+#include <string.h>
+
+/************************************************************************/
+/* pj_datum_set() */
+/************************************************************************/
+
+int pj_datum_set(paralist *pl, PJ *projdef)
+
+{
+ const char *name, *towgs84, *nadgrids;
+
+ projdef->datum_type = PJD_UNKNOWN;
+
+/* -------------------------------------------------------------------- */
+/* Is there a datum definition in the parameters list? If so, */
+/* add the defining values to the parameter list. Note that */
+/* this will append the ellipse definition as well as the */
+/* towgs84= and related parameters. It should also be pointed */
+/* out that the addition is permanent rather than temporary */
+/* like most other keyword expansion so that the ellipse */
+/* definition will last into the pj_ell_set() function called */
+/* after this one. */
+/* -------------------------------------------------------------------- */
+ if( (name = pj_param(pl,"sdatum").s) != NULL )
+ {
+ paralist *curr;
+ const char *s;
+ int i;
+
+ /* find the end of the list, so we can add to it */
+ for (curr = pl; curr && curr->next ; curr = curr->next) {}
+
+ /* find the datum definition */
+ for (i = 0; (s = pj_datums[i].id) && strcmp(name, s) ; ++i) {}
+
+ if (!s) { pj_errno = -9; return 1; }
+
+ if( pj_datums[i].ellipse_id && strlen(pj_datums[i].ellipse_id) > 0 )
+ {
+ char entry[100];
+
+ strcpy( entry, "ellps=" );
+ strncat( entry, pj_datums[i].ellipse_id, 80 );
+ curr = curr->next = pj_mkparam(entry);
+ }
+
+ if( pj_datums[i].defn && strlen(pj_datums[i].defn) > 0 )
+ curr = curr->next = pj_mkparam(pj_datums[i].defn);
+ }
+
+/* -------------------------------------------------------------------- */
+/* Check for nadgrids parameter. */
+/* -------------------------------------------------------------------- */
+ if( (nadgrids = pj_param(pl,"snadgrids").s) != NULL )
+ {
+ /* We don't actually save the value separately. It will continue
+ to exist int he param list for use in pj_apply_gridshift.c */
+
+ projdef->datum_type = PJD_GRIDSHIFT;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Check for towgs84 parameter. */
+/* -------------------------------------------------------------------- */
+ else if( (towgs84 = pj_param(pl,"stowgs84").s) != NULL )
+ {
+ int parm_count = 0;
+ const char *s;
+
+ memset( projdef->datum_params, 0, sizeof(double) * 7);
+
+ /* parse out the parameters */
+ s = towgs84;
+ for( s = towgs84; *s != '\0'; )
+ {
+ projdef->datum_params[parm_count++] = atof(s);
+ while( *s != '\0' && *s != ',' )
+ s++;
+ if( *s == ',' )
+ s++;
+ }
+
+ if( projdef->datum_params[3] != 0.0
+ || projdef->datum_params[4] != 0.0
+ || projdef->datum_params[5] != 0.0
+ || projdef->datum_params[6] != 0.0 )
+ projdef->datum_type = PJD_7PARAM;
+ else
+ projdef->datum_type = PJD_3PARAM;
+
+ /* Note that pj_init() will later switch datum_type to
+ PJD_WGS84 if shifts are all zero, and ellipsoid is WGS84 or GRS80 */
+ }
+
+ return 0;
+}
diff --git a/src/pj_datums.c b/src/pj_datums.c
new file mode 100644
index 00000000..f8f8385e
--- /dev/null
+++ b/src/pj_datums.c
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: PROJ.4
+ * Purpose: Built in datum list.
+ * Author: Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log$
+ * Revision 1.1 2000/07/06 23:32:27 warmerda
+ * New
+ *
+ */
+
+#define PJ_DATUMS__
+
+#include <projects.h>
+
+/*
+ * The ellipse code must match one from pj_ellps.c. The datum id should
+ * be kept to 12 characters or less if possible. Use the official OGC
+ * datum name for the comments if available.
+ */
+
+struct PJ_DATUMS pj_datums[] = {
+/* id definition ellipse comments */
+/* -- ---------- ------- -------- */
+"WGS84", "towgs84=0,0,0", "WGS84", "",
+"GGRS87", "towgs84=-199.87,74.79,246.62", "GRS80",
+ "Greek_Geodetic_Reference_System_1987",
+"NAD83", "towgs84=0,0,0", "GRS80",
+ "North_American_Datum_1983",
+"NAD27", "nadgrids=conus", "clrk66",
+ "North_American_Datum_1927",
+NULL, NULL, NULL, NULL
+};
diff --git a/src/pj_latlong.c b/src/pj_latlong.c
new file mode 100644
index 00000000..554691d0
--- /dev/null
+++ b/src/pj_latlong.c
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: PROJ.4
+ * Purpose: Stub projection implementation for lat/long coordinates. We
+ * don't actually change the coordinates, but we want proj=latlong
+ * to act sort of like a projection.
+ * Author: Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log$
+ * Revision 1.1 2000/07/06 23:32:27 warmerda
+ * New
+ *
+ */
+
+/* very loosely based upon DMA code by Bradford W. Drew */
+#define PJ_LIB__
+#include <projects.h>
+PROJ_HEAD(latlong, "Lat/long (Geodetic)") "\n\t";
+
+FORWARD(forward);
+
+ xy.x = lp.lam / P->a;
+ xy.y = lp.phi / P->a;
+ return xy;
+}
+INVERSE(inverse);
+
+ lp.phi = xy.y * P->a;
+ lp.lam = xy.x * P->a;
+ return lp;
+}
+FREEUP; if (P) pj_dalloc(P); }
+ENTRY0(latlong)
+ P->is_latlong = 1;
+ P->x0 = 0.0;
+ P->y0 = 0.0;
+ P->inv = inverse; P->fwd = forward;
+ENDENTRY(P)
diff --git a/src/pj_transform.c b/src/pj_transform.c
new file mode 100644
index 00000000..9bdd4314
--- /dev/null
+++ b/src/pj_transform.c
@@ -0,0 +1,459 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project: PROJ.4
+ * Purpose: Perform overall coordinate system to coordinate system
+ * transformations (pj_transform() function) including reprojection
+ * and datum shifting.
+ * Author: Frank Warmerdam, warmerda@home.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ******************************************************************************
+ *
+ * $Log$
+ * Revision 1.1 2000/07/06 23:32:27 warmerda
+ * New
+ *
+ */
+
+#include <projects.h>
+#include <string.h>
+#include <math.h>
+#include "geocent.h"
+
+#ifndef SRS_WGS84_SEMIMAJOR
+#define SRS_WGS84_SEMIMAJOR 6378137.0
+#endif
+
+#ifndef SRS_WGS84_ESQUARED
+#define SRS_WGS84_ESQUARED 0.006694379990
+#endif
+
+/************************************************************************/
+/* pj_transform() */
+/* */
+/* Currently this function doesn't recognise if two projections */
+/* are identical (to short circuit reprojection) because it is */
+/* difficult to compare PJ structures (since there are some */
+/* projection specific components). */
+/************************************************************************/
+
+int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ long i;
+ int need_datum_shift;
+
+ pj_errno = 0;
+
+ if( point_offset == 0 )
+ point_offset = 1;
+
+/* -------------------------------------------------------------------- */
+/* Transform source points to lat/long, if they aren't */
+/* already. */
+/* -------------------------------------------------------------------- */
+ if( !srcdefn->is_latlong )
+ {
+ for( i = 0; i < point_count; i++ )
+ {
+ XY projected_loc;
+ LP geodetic_loc;
+
+ projected_loc.u = x[point_offset*i];
+ projected_loc.v = y[point_offset*i];
+
+ geodetic_loc = pj_inv( projected_loc, srcdefn );
+ if( pj_errno != 0 )
+ return pj_errno;
+
+ x[point_offset*i] = geodetic_loc.u;
+ y[point_offset*i] = geodetic_loc.v;
+ }
+ }
+
+/* -------------------------------------------------------------------- */
+/* Convert datums if needed, and possible. */
+/* -------------------------------------------------------------------- */
+ if( pj_datum_transform( srcdefn, dstdefn, point_count, point_offset,
+ x, y, z ) != 0 )
+ return pj_errno;
+
+/* -------------------------------------------------------------------- */
+/* Transform destination points to projection coordinates, if */
+/* desired. */
+/* -------------------------------------------------------------------- */
+ if( !dstdefn->is_latlong )
+ {
+ for( i = 0; i < point_count; i++ )
+ {
+ XY projected_loc;
+ LP geodetic_loc;
+
+ geodetic_loc.u = x[point_offset*i];
+ geodetic_loc.v = y[point_offset*i];
+
+ projected_loc = pj_fwd( geodetic_loc, dstdefn );
+ if( pj_errno != 0 )
+ return pj_errno;
+
+ x[point_offset*i] = projected_loc.u;
+ y[point_offset*i] = projected_loc.v;
+ }
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/* pj_geodetic_to_geocentric() */
+/************************************************************************/
+
+int pj_geodetic_to_geocentric( double a, double es,
+ long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ double b;
+ int i;
+
+ if( es == 0.0 )
+ b = a;
+ else
+ b = a * sqrt(1-es);
+
+ if( Set_Geocentric_Parameters( a, b ) != 0 )
+ {
+ pj_errno = PJD_ERR_GEOCENTRIC;
+ return pj_errno;
+ }
+
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+
+ if( Convert_Geodetic_To_Geocentric( y[io], x[io], z[io],
+ x+io, y+io, z+io ) != 0 )
+ {
+ pj_errno = PJD_ERR_GEOCENTRIC;
+ return PJD_ERR_GEOCENTRIC;
+ }
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/* pj_geodetic_to_geocentric() */
+/************************************************************************/
+
+int pj_geocentric_to_geodetic( double a, double es,
+ long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ double b;
+ int i;
+
+ if( es == 0.0 )
+ b = a;
+ else
+ b = a * sqrt(1-es);
+
+ if( Set_Geocentric_Parameters( a, b ) != 0 )
+ {
+ pj_errno = PJD_ERR_GEOCENTRIC;
+ return pj_errno;
+ }
+
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+
+ Convert_Geocentric_To_Geodetic( x[io], y[io], z[io],
+ y+io, x+io, z+io );
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/* pj_compare_datums() */
+/* */
+/* Returns TRUE if the two datums are identical, otherwise */
+/* FALSE. */
+/************************************************************************/
+
+int pj_compare_datums( PJ *srcdefn, PJ *dstdefn )
+
+{
+ if( srcdefn->datum_type != dstdefn->datum_type )
+ {
+ return 0;
+ }
+ else if( srcdefn->a != dstdefn->a
+ || ABS(srcdefn->es - dstdefn->es) > 0.000000000050 )
+ {
+ /* the tolerence for es is to ensure that GRS80 and WGS84 are
+ considered identical */
+ return 0;
+ }
+ else if( srcdefn->datum_type == PJD_3PARAM )
+ {
+ return (srcdefn->datum_params[0] == dstdefn->datum_params[0]
+ && srcdefn->datum_params[1] == dstdefn->datum_params[1]
+ && srcdefn->datum_params[2] == dstdefn->datum_params[2]);
+ }
+ else if( srcdefn->datum_type == PJD_7PARAM )
+ {
+ return (srcdefn->datum_params[0] == dstdefn->datum_params[0]
+ && srcdefn->datum_params[1] == dstdefn->datum_params[1]
+ && srcdefn->datum_params[2] == dstdefn->datum_params[2]
+ && srcdefn->datum_params[3] == dstdefn->datum_params[3]
+ && srcdefn->datum_params[4] == dstdefn->datum_params[4]
+ && srcdefn->datum_params[5] == dstdefn->datum_params[5]
+ && srcdefn->datum_params[6] == dstdefn->datum_params[6]);
+ }
+ else if( srcdefn->datum_type == PJD_GRIDSHIFT )
+ {
+ return strcmp( pj_param(srcdefn->params,"snadgrids").s,
+ pj_param(dstdefn->params,"snadgrids").s ) == 0;
+ }
+ else
+ return 1;
+}
+
+/************************************************************************/
+/* pj_geocentic_to_wgs84() */
+/************************************************************************/
+
+int pj_geocentric_to_wgs84( PJ *defn,
+ long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ int i;
+
+ pj_errno = 0;
+
+ if( defn->datum_type == PJD_3PARAM )
+ {
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+
+ x[io] = x[io] + defn->datum_params[0];
+ y[io] = y[io] + defn->datum_params[1];
+ z[io] = z[io] + defn->datum_params[2];
+ }
+ }
+ else if( defn->datum_type == PJD_7PARAM )
+ {
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+ double x_out, y_out, z_out;
+
+ x_out = x[io] + defn->datum_params[0]
+ + y[io] * defn->datum_params[5]
+ + z[io] * defn->datum_params[4]
+ + x[io] * defn->datum_params[6];
+
+ y_out = y[io] + defn->datum_params[1]
+ + x[io] * defn->datum_params[5]
+ + z[io] * defn->datum_params[3]
+ + y[io] * defn->datum_params[6];
+
+ z_out = z[io] + defn->datum_params[2]
+ + x[io] * defn->datum_params[4]
+ + y[io] * defn->datum_params[3]
+ + z[io] * defn->datum_params[6];
+
+ x[io] = x_out;
+ y[io] = y_out;
+ z[io] = z_out;
+ }
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/* pj_geocentic_from_wgs84() */
+/************************************************************************/
+
+int pj_geocentric_from_wgs84( PJ *defn,
+ long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ int i;
+
+ pj_errno = 0;
+
+ if( defn->datum_type == PJD_3PARAM )
+ {
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+
+ x[io] = x[io] - defn->datum_params[0];
+ y[io] = y[io] - defn->datum_params[1];
+ z[io] = z[io] - defn->datum_params[2];
+ }
+ }
+ else if( defn->datum_type == PJD_7PARAM )
+ {
+ for( i = 0; i < point_count; i++ )
+ {
+ long io = i * point_offset;
+ double x_out, y_out, z_out;
+
+ x_out = x[io] - defn->datum_params[0]
+ - y[io] * defn->datum_params[5]
+ - z[io] * defn->datum_params[4]
+ - x[io] * defn->datum_params[6];
+
+ y_out = y[io] - defn->datum_params[1]
+ - x[io] * defn->datum_params[5]
+ - z[io] * defn->datum_params[3]
+ - y[io] * defn->datum_params[6];
+
+ z_out = z[io] - defn->datum_params[2]
+ - x[io] * defn->datum_params[4]
+ - y[io] * defn->datum_params[3]
+ - z[io] * defn->datum_params[6];
+
+ x[io] = x_out;
+ y[io] = y_out;
+ z[io] = z_out;
+ }
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/* pj_datum_transform() */
+/************************************************************************/
+
+int pj_datum_transform( PJ *srcdefn, PJ *dstdefn,
+ long point_count, int point_offset,
+ double *x, double *y, double *z )
+
+{
+ double src_a, src_es, dst_a, dst_es;
+
+ pj_errno = 0;
+
+/* -------------------------------------------------------------------- */
+/* Short cut if the datums are identical. */
+/* -------------------------------------------------------------------- */
+ if( pj_compare_datums( srcdefn, dstdefn ) )
+ return 0;
+
+ src_a = srcdefn->a;
+ src_es = srcdefn->es;
+
+ dst_a = dstdefn->a;
+ dst_es = dstdefn->es;
+
+/* -------------------------------------------------------------------- */
+/* If this datum requires grid shifts, then apply it to geodetic */
+/* coordinates. */
+/* -------------------------------------------------------------------- */
+ if( srcdefn->datum_type == PJD_GRIDSHIFT )
+ {
+ pj_apply_gridshift( pj_param(srcdefn->params,"snadgrids").s, 0,
+ point_count, point_offset, x, y, z );
+
+ if( pj_errno != 0 )
+ return pj_errno;
+
+ src_a = SRS_WGS84_SEMIMAJOR;
+ src_es = 0.006694379990;
+ }
+
+ if( dstdefn->datum_type == PJD_GRIDSHIFT )
+ {
+ dst_a = SRS_WGS84_SEMIMAJOR;
+ dst_es = 0.006694379990;
+ }
+
+/* ==================================================================== */
+/* Do we need to go through geocentric coordinates? */
+/* ==================================================================== */
+ if( srcdefn->datum_type == PJD_3PARAM
+ || srcdefn->datum_type == PJD_7PARAM
+ || dstdefn->datum_type == PJD_3PARAM
+ || dstdefn->datum_type == PJD_7PARAM)
+ {
+/* -------------------------------------------------------------------- */
+/* Convert to geocentric coordinates. */
+/* -------------------------------------------------------------------- */
+ pj_geodetic_to_geocentric( src_a, src_es,
+ point_count, point_offset, x, y, z );
+
+ if( pj_errno )
+ return pj_errno;
+
+/* -------------------------------------------------------------------- */
+/* Convert between datums. */
+/* -------------------------------------------------------------------- */
+ if( srcdefn->datum_type != PJD_UNKNOWN
+ && dstdefn->datum_type != PJD_UNKNOWN )
+ {
+ pj_geocentric_to_wgs84( srcdefn, point_count, point_offset,x,y,z);
+ if( pj_errno != 0 )
+ return pj_errno;
+
+ pj_geocentric_from_wgs84( dstdefn, point_count,point_offset,x,y,z);
+ if( pj_errno != 0 )
+ return pj_errno;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Convert back to geodetic coordinates. */
+/* -------------------------------------------------------------------- */
+ pj_geocentric_to_geodetic( dst_a, dst_es,
+ point_count, point_offset, x, y, z );
+
+ if( pj_errno )
+ return pj_errno;
+ }
+
+/* -------------------------------------------------------------------- */
+/* Apply grid shift to destination if required. */
+/* -------------------------------------------------------------------- */
+ if( dstdefn->datum_type == PJD_GRIDSHIFT )
+ {
+ pj_apply_gridshift( pj_param(dstdefn->params,"snadgrids").s, 1,
+ point_count, point_offset, x, y, z );
+
+ if( pj_errno != 0 )
+ return pj_errno;
+ }
+
+ return 0;
+}
+