/****************************************************************************** * $Id: pj_init.c,v 1.19 2007/11/26 00:21:59 fwarmerdam Exp $ * * Project: PROJ.4 * Purpose: Initialize projection object from string definition. Includes * pj_init(), pj_init_plus() and pj_free() function. * Author: Gerald Evenden, Frank Warmerdam <warmerdam@pobox.com> * ****************************************************************************** * Copyright (c) 1995, Gerald Evenden * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com> * * 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: pj_init.c,v $ * Revision 1.19 2007/11/26 00:21:59 fwarmerdam * Modified PJ structure to hold a_orig, es_orig, ellipsoid definition before * adjustment for spherical projections. * Modified pj_datum_transform() to use the original ellipsoid parameters, * not the ones adjusted for spherical projections. * Modified pj_datum_transform() to not attempt any datum shift via * geocentric coordinates if the source *or* destination are raw ellipsoids * (ie. PJD_UNKNOWN). All per PROJ bug #1602, GDAL bug #2025. * * Revision 1.18 2006/10/12 21:04:39 fwarmerdam * Added experimental +lon_wrap argument to set a "center point" for * longitude wrapping of longitude values coming out of pj_transform(). * * Revision 1.17 2006/09/22 23:06:24 fwarmerdam * remote static start variable in pj_init (bug 1283) * * Revision 1.16 2004/09/08 15:23:37 warmerda * added new error for unknown prime meridians * * Revision 1.15 2004/05/05 01:45:41 warmerda * Made sword even longer. * * Revision 1.14 2004/05/05 01:45:00 warmerda * Make sword buffer larger so long +towgs84 parameters don't get split. * * Revision 1.13 2003/09/16 03:46:21 warmerda * dont use default ellps if any earth model info is set: bug 386 * * Revision 1.12 2003/08/21 02:15:59 warmerda * improve MAX_ARG checking * * Revision 1.11 2003/06/09 21:23:16 warmerda * ensure start is initialized at very beginning of pj_init() * * Revision 1.10 2003/03/16 16:38:24 warmerda * Modified get_opt() to terminate reading the definition when a new * definition (a word starting with '<') is encountered, in addition to when * the definition terminator '<>' is encountered, so that unterminated * definitions like those in the distributed esri file will work properly. * http://bugzilla.remotesensing.org/show_bug.cgi?id=302 * * Revision 1.9 2002/12/14 20:15:02 warmerda * added geocentric support, updated headers * */ #define PJ_LIB__ #include "projects.h" #include <stdio.h> #include <string.h> #include <errno.h> PJ_CVSID("$Id: pj_init.c,v 1.19 2007/11/26 00:21:59 fwarmerdam Exp $"); extern FILE *pj_open_lib(char *, char *); /************************************************************************/ /* get_opt() */ /************************************************************************/ static paralist * get_opt(paralist **start, FILE *fid, char *name, paralist *next) { char sword[302], *word = sword+1; int first = 1, len, c; len = strlen(name); *sword = 't'; while (fscanf(fid, "%300s", word) == 1) { if (*word == '#') /* skip comments */ while((c = fgetc(fid)) != EOF && c != '\n') ; else if (*word == '<') { /* control name */ if (first && !strncmp(name, word + 1, len) && word[len + 1] == '>') first = 0; else if (!first && *word == '<') { while((c = fgetc(fid)) != EOF && c != '\n') ; break; } } else if (!first && !pj_param(*start, sword).i) { /* don't default ellipse if datum, ellps or any earth model information is set. */ if( strncmp(word,"ellps=",6) != 0 || (!pj_param(*start, "tdatum").i && !pj_param(*start, "tellps").i && !pj_param(*start, "ta").i && !pj_param(*start, "tb").i && !pj_param(*start, "trf").i && !pj_param(*start, "tf").i) ) { next = next->next = pj_mkparam(word); } } } if (errno == 25) errno = 0; return next; } /************************************************************************/ /* get_defaults() */ /************************************************************************/ static paralist * get_defaults(paralist **start, paralist *next, char *name) { FILE *fid; if (fid = pj_open_lib("proj_def.dat", "rt")) { next = get_opt(start, fid, "general", next); rewind(fid); next = get_opt(start, fid, name, next); (void)fclose(fid); } if (errno) errno = 0; /* don't care if can't open file */ return next; } /************************************************************************/ /* get_init() */ /************************************************************************/ static paralist * get_init(paralist **start, paralist *next, char *name) { char fname[MAX_PATH_FILENAME+ID_TAG_MAX+3], *opt; FILE *fid; (void)strncpy(fname, name, MAX_PATH_FILENAME + ID_TAG_MAX + 1); if (opt = strrchr(fname, ':')) *opt++ = '\0'; else { pj_errno = -3; return(0); } if (fid = pj_open_lib(fname, "rt")) next = get_opt(start, fid, opt, next); else return(0); (void)fclose(fid); if (errno == 25) errno = 0; /* unknown problem with some sys errno<-25 */ return next; } /************************************************************************/ /* pj_init_plus() */ /* */ /* Same as pj_init() except it takes one argument string with */ /* individual arguments preceeded by '+', such as "+proj=utm */ /* +zone=11 +ellps=WGS84". */ /************************************************************************/ PJ * pj_init_plus( const char *definition ) { #define MAX_ARG 200 char *argv[MAX_ARG]; char *defn_copy; int argc = 0, i; PJ *result; /* make a copy that we can manipulate */ defn_copy = (char *) pj_malloc( strlen(definition)+1 ); strcpy( defn_copy, definition ); /* split into arguments based on '+' and trim white space */ for( i = 0; defn_copy[i] != '\0'; i++ ) { switch( defn_copy[i] ) { case '+': if( i == 0 || defn_copy[i-1] == '\0' ) { if( argc+1 == MAX_ARG ) { pj_errno = -44; return NULL; } argv[argc++] = defn_copy + i + 1; } break; case ' ': case '\t': case '\n': defn_copy[i] = '\0'; break; default: /* do nothing */; } } /* perform actual initialization */ result = pj_init( argc, argv ); pj_dalloc( defn_copy ); return result; } /************************************************************************/ /* pj_init() */ /* */ /* Main entry point for initialing a PJ projections */ /* definition. Note that the projection specific function is */ /* called to do the initial allocation so it can be created */ /* large enough to hold projection specific parameters. */ /************************************************************************/ PJ * pj_init(int argc, char **argv) { char *s, *name; paralist *start = NULL; PJ *(*proj)(PJ *); paralist *curr = 0; int i; PJ *PIN = 0; errno = pj_errno = 0; start = NULL; /* put arguments into internal linked list */ if (argc <= 0) { pj_errno = -1; goto bum_call; } for (i = 0; i < argc; ++i) if (i) curr = curr->next = pj_mkparam(argv[i]); else start = curr = pj_mkparam(argv[i]); if (pj_errno) goto bum_call; /* check if +init present */ if (pj_param(start, "tinit").i) { paralist *last = curr; if (!(curr = get_init(&start, curr, pj_param(start, "sinit").s))) goto bum_call; if (curr == last) { pj_errno = -2; goto bum_call; } } /* find projection selection */ if (!(name = pj_param(start, "sproj").s)) { pj_errno = -4; goto bum_call; } for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ; if (!s) { pj_errno = -5; goto bum_call; } /* set defaults, unless inhibited */ if (!pj_param(start, "bno_defs").i) curr = get_defaults(&start, curr, name); proj = (PJ *(*)(PJ *)) pj_list[i].proj; /* allocate projection structure */ if (!(PIN = (*proj)(0))) goto bum_call; PIN->params = start; PIN->is_latlong = 0; PIN->is_geocent = 0; PIN->long_wrap_center = 0.0; /* set datum parameters */ if (pj_datum_set(start, PIN)) goto bum_call; /* set ellipsoid/sphere parameters */ if (pj_ell_set(start, &PIN->a, &PIN->es)) goto bum_call; PIN->a_orig = PIN->a; PIN->es_orig = PIN->es; PIN->e = sqrt(PIN->es); PIN->ra = 1. / PIN->a; PIN->one_es = 1. - PIN->es; if (PIN->one_es == 0.) { pj_errno = -6; goto bum_call; } PIN->rone_es = 1./PIN->one_es; /* Now that we have ellipse information check for WGS84 datum */ if( PIN->datum_type == PJD_3PARAM && PIN->datum_params[0] == 0.0 && PIN->datum_params[1] == 0.0 && PIN->datum_params[2] == 0.0 && PIN->a == 6378137.0 && ABS(PIN->es - 0.006694379990) < 0.000000000050 )/*WGS84/GRS80*/ { PIN->datum_type = PJD_WGS84; } /* set PIN->geoc coordinate system */ PIN->geoc = (PIN->es && pj_param(start, "bgeoc").i); /* over-ranging flag */ PIN->over = pj_param(start, "bover").i; /* longitude center for wrapping */ PIN->long_wrap_center = pj_param(start, "rlon_wrap").f; /* central meridian */ PIN->lam0=pj_param(start, "rlon_0").f; /* central latitude */ PIN->phi0 = pj_param(start, "rlat_0").f; /* false easting and northing */ PIN->x0 = pj_param(start, "dx_0").f; PIN->y0 = pj_param(start, "dy_0").f; /* general scaling factor */ if (pj_param(start, "tk_0").i) PIN->k0 = pj_param(start, "dk_0").f; else if (pj_param(start, "tk").i) PIN->k0 = pj_param(start, "dk").f; else PIN->k0 = 1.; if (PIN->k0 <= 0.) { pj_errno = -31; goto bum_call; } /* set units */ s = 0; if (name = pj_param(start, "sunits").s) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ; if (!s) { pj_errno = -7; goto bum_call; } s = pj_units[i].to_meter; } if (s || (s = pj_param(start, "sto_meter").s)) { PIN->to_meter = strtod(s, &s); if (*s == '/') /* ratio number */ PIN->to_meter /= strtod(++s, 0); PIN->fr_meter = 1. / PIN->to_meter; } else PIN->to_meter = PIN->fr_meter = 1.; /* prime meridian */ s = 0; if (name = pj_param(start, "spm").s) { const char *value = NULL; char *next_str = NULL; for (i = 0; pj_prime_meridians[i].id != NULL; ++i ) { if( strcmp(name,pj_prime_meridians[i].id) == 0 ) { value = pj_prime_meridians[i].defn; break; } } if( value == NULL && (dmstor(name,&next_str) != 0.0 || *name == '0') && *next_str == '\0' ) value = name; if (!value) { pj_errno = -46; goto bum_call; } PIN->from_greenwich = dmstor(value,NULL); } else PIN->from_greenwich = 0.0; /* projection specific initialization */ if (!(PIN = (*proj)(PIN)) || errno || pj_errno) { bum_call: /* cleanup error return */ if (!pj_errno) pj_errno = errno; if (PIN) pj_free(PIN); else for ( ; start; start = curr) { curr = start->next; pj_dalloc(start); } PIN = 0; } return PIN; } /************************************************************************/ /* pj_free() */ /* */ /* This is the application callable entry point for destroying */ /* a projection definition. It does work generic to all */ /* projection types, and then calls the projection specific */ /* free function (P->pfree()) to do local work. This maps to */ /* the FREEUP code in the individual projection source files. */ /************************************************************************/ void pj_free(PJ *P) { if (P) { paralist *t = P->params, *n; /* free parameter list elements */ for (t = P->params; t; t = n) { n = t->next; pj_dalloc(t); } /* free projection parameters */ P->pfree(P); } }