static PJ_COORD todeg_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { size_t i, n; const char *axis = "enut"; paralist *l = pj_param_exists (P->params, "axis"); if (l && dir==PJ_FWD) axis = l->param + strlen ("axis="); n = strlen (axis); for (i = 0; i < n; i++) if (strchr ("news", axis[i])) a.v[i] = proj_todeg (a.v[i]); return a; }
static PJ* read_convention(PJ* P) { struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *)P->opaque; /* In case there are rotational terms, we require an explicit convention * to be provided. */ if (!Q->no_rotation) { const char* convention = pj_param (P->ctx, P->params, "sconvention").s; if( !convention ) { proj_log_error (P, "helmert: missing 'convention' argument"); return pj_default_destructor (P, PJD_ERR_MISSING_ARGS); } if( strcmp(convention, "position_vector") == 0 ) { Q->is_position_vector = 1; } else if( strcmp(convention, "coordinate_frame") == 0 ) { Q->is_position_vector = 0; } else { proj_log_error (P, "helmert: invalid value for 'convention' argument"); return pj_default_destructor (P, PJD_ERR_INVALID_ARG); } /* historically towgs84 in PROJ has always been using position_vector * convention. Accepting coordinate_frame would be confusing. */ if (pj_param_exists (P->params, "towgs84")) { if( !Q->is_position_vector ) { proj_log_error (P, "helmert: towgs84 should only be used with " "convention=position_vector"); return pj_default_destructor (P, PJD_ERR_INVALID_ARG); } } } return P; }
static int cs2cs_emulation_setup (PJ *P) { /************************************************************************************** If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the 4D API equivalent operations, so the preparation and finalization steps in the pj_inv/pj_fwd invocators can emulate the behaviour of pj_transform and the cs2cs app. Returns 1 on success, 0 on failure **************************************************************************************/ PJ *Q; paralist *p; int do_cart = 0; if (0==P) return 0; /* Don't recurse when calling proj_create (which calls us back) */ if (pj_param_exists (P->params, "break_cs2cs_recursion")) return 1; /* Swap axes? */ p = pj_param_exists (P->params, "axis"); /* Don't axisswap if data are already in "enu" order */ if (p && (0!=strcmp ("enu", p->param))) { char *def = malloc (100+strlen(P->axis)); if (0==def) return 0; sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); Q = proj_create (P->ctx, def); free (def); if (0==Q) return 0; P->axisswap = skip_prep_fin(Q); } /* Geoid grid(s) given? */ p = pj_param_exists (P->params, "geoidgrids"); if (p && strlen (p->param) > strlen ("geoidgrids=")) { char *gridnames = p->param + strlen ("geoidgrids="); char *def = malloc (100+strlen(gridnames)); if (0==def) return 0; sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames); Q = proj_create (P->ctx, def); free (def); if (0==Q) return 0; P->vgridshift = skip_prep_fin(Q); } /* Datum shift grid(s) given? */ p = pj_param_exists (P->params, "nadgrids"); if (p && strlen (p->param) > strlen ("nadgrids=")) { char *gridnames = p->param + strlen ("nadgrids="); char *def = malloc (100+strlen(gridnames)); if (0==def) return 0; sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames); Q = proj_create (P->ctx, def); free (def); if (0==Q) return 0; P->hgridshift = skip_prep_fin(Q); } /* We ignore helmert if we have grid shift */ p = P->hgridshift ? 0 : pj_param_exists (P->params, "towgs84"); while (p) { char *def; char *s = p->param; double *d = P->datum_params; size_t n = strlen (s); /* We ignore null helmert shifts (common in auto-translated resource files, e.g. epsg) */ if (0==d[0] && 0==d[1] && 0==d[2] && 0==d[3] && 0==d[4] && 0==d[5] && 0==d[6]) { /* If the current ellipsoid is not WGS84, then make sure the */ /* change in ellipsoid is still done. */ if (!(fabs(P->a_orig - 6378137.0) < 1e-8 && fabs(P->es_orig - 0.0066943799901413) < 1e-15)) { do_cart = 1; } break; } if (n <= 8) /* 8==strlen ("towgs84=") */ return 0; def = malloc (100+n); if (0==def) return 0; sprintf (def, "break_cs2cs_recursion proj=helmert exact %s transpose", s); Q = proj_create (P->ctx, def); pj_inherit_ellipsoid_def (P, Q); free (def); if (0==Q) return 0; P->helmert = skip_prep_fin (Q); break; } /* We also need cartesian/geographical transformations if we are working in */ /* geocentric/cartesian space or we need to do a Helmert transform. */ if (P->is_geocent || P->helmert || do_cart) { char def[150]; sprintf (def, "break_cs2cs_recursion proj=cart a=%40.20g es=%40.20g", P->a_orig, P->es_orig); Q = proj_create (P->ctx, def); if (0==Q) return 0; P->cart = skip_prep_fin (Q); sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); Q = proj_create (P->ctx, def); if (0==Q) return 0; P->cart_wgs84 = skip_prep_fin (Q); } return 1; }
PJ * pj_init_ctx(projCtx ctx, int argc, char **argv) { char *s, *name; PJ_CONSTRUCTOR proj; paralist *curr, *init, *start; int i; int err; PJ *PIN = 0; int n_pipelines = 0; int n_inits = 0; if (0==ctx) ctx = pj_get_default_ctx (); ctx->last_errno = 0; if (argc <= 0) { pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS); return 0; } /* count occurrences of pipelines and inits */ for (i = 0; i < argc; ++i) { if (!strcmp (argv[i], "+proj=pipeline") || !strcmp(argv[i], "proj=pipeline") ) n_pipelines++; if (!strncmp (argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5)) n_inits++; } /* can't have nested pipelines directly */ if (n_pipelines > 1) { pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE); return 0; } /* don't allow more than one +init in non-pipeline operations */ if (n_pipelines == 0 && n_inits > 1) { pj_ctx_set_errno (ctx, PJD_ERR_TOO_MANY_INITS); return 0; } /* put arguments into internal linked list */ start = curr = pj_mkparam(argv[0]); if (!curr) return pj_dealloc_params (ctx, start, ENOMEM); for (i = 1; i < argc; ++i) { curr->next = pj_mkparam(argv[i]); if (!curr->next) return pj_dealloc_params (ctx, start, ENOMEM); curr = curr->next; } /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */ /* expanded in the individual pipeline steps during pipeline initialization. */ /* Potentially this leads to many nested pipelines, which shouldn't be a */ /* problem when '+init's are expanded as late as possible. */ init = pj_param_exists (start, "init"); if (init && n_pipelines == 0) { init = pj_expand_init (ctx, init); if (!init) return pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS); } if (ctx->last_errno) return pj_dealloc_params (ctx, start, ctx->last_errno); /* Find projection selection */ curr = pj_param_exists (start, "proj"); if (0==curr) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); name = curr->param; if (strlen (name) < 6) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); name += 5; proj = locate_constructor (name); if (0==proj) return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); /* Append general and projection specific defaults to the definition list */ append_defaults_to_paralist (ctx, start, "general"); append_defaults_to_paralist (ctx, start, name); /* Allocate projection structure */ PIN = proj(0); if (0==PIN) return pj_dealloc_params (ctx, start, ENOMEM); PIN->ctx = ctx; PIN->params = start; PIN->is_latlong = 0; PIN->is_geocent = 0; PIN->is_long_wrap_set = 0; PIN->long_wrap_center = 0.0; strcpy( PIN->axis, "enu" ); PIN->gridlist = NULL; PIN->gridlist_count = 0; PIN->vgridlist_geoid = NULL; PIN->vgridlist_geoid_count = 0; /* Set datum parameters. Similarly to +init parameters we want to expand */ /* +datum parameters as late as possible when dealing with pipelines. */ /* otherwise only the first occurrence of +datum will be expanded and that */ if (n_pipelines == 0) { if (pj_datum_set(ctx, start, PIN)) return pj_default_destructor (PIN, proj_errno(PIN)); } err = pj_ellipsoid (PIN); if (err) { /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */ if (PIN->need_ellps) { pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); return pj_default_destructor (PIN, proj_errno(PIN)); } else { if (PJD_ERR_MAJOR_AXIS_NOT_GIVEN==proj_errno (PIN)) proj_errno_reset (PIN); PIN->f = 1.0/298.257223563; PIN->a_orig = PIN->a = 6378137.0; PIN->es_orig = PIN->es = PIN->f*(2-PIN->f); } } PIN->a_orig = PIN->a; PIN->es_orig = PIN->es; if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es)) return pj_default_destructor (PIN, PJD_ERR_ECCENTRICITY_IS_ONE); /* 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 != 0.0 && pj_param(ctx, start, "bgeoc").i); /* Over-ranging flag */ PIN->over = pj_param(ctx, start, "bover").i; /* Vertical datum geoid grids */ PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i; if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */ pj_param(ctx, start, "sgeoidgrids"); /* Longitude center for wrapping */ PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i; if (PIN->is_long_wrap_set) { PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f; /* Don't accept excessive values otherwise we might perform badly */ /* when correcting longitudes around it */ /* The test is written this way to error on long_wrap_center "=" NaN */ if( !(fabs(PIN->long_wrap_center) < 10 * M_TWOPI) ) return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); } /* Axis orientation */ if( (pj_param(ctx, start,"saxis").s) != NULL ) { const char *axis_legal = "ewnsud"; const char *axis_arg = pj_param(ctx, start,"saxis").s; if( strlen(axis_arg) != 3 ) return pj_default_destructor (PIN, PJD_ERR_AXIS); if( strchr( axis_legal, axis_arg[0] ) == NULL || strchr( axis_legal, axis_arg[1] ) == NULL || strchr( axis_legal, axis_arg[2] ) == NULL) return pj_default_destructor (PIN, PJD_ERR_AXIS); /* TODO: it would be nice to validate we don't have on axis repeated */ strcpy( PIN->axis, axis_arg ); } /* Central meridian */ PIN->lam0=pj_param(ctx, start, "rlon_0").f; /* Central latitude */ PIN->phi0 = pj_param(ctx, start, "rlat_0").f; /* False easting and northing */ PIN->x0 = pj_param(ctx, start, "dx_0").f; PIN->y0 = pj_param(ctx, start, "dy_0").f; PIN->z0 = pj_param(ctx, start, "dz_0").f; PIN->t0 = pj_param(ctx, start, "dt_0").f; /* General scaling factor */ if (pj_param(ctx, start, "tk_0").i) PIN->k0 = pj_param(ctx, start, "dk_0").f; else if (pj_param(ctx, start, "tk").i) PIN->k0 = pj_param(ctx, start, "dk").f; else PIN->k0 = 1.; if (PIN->k0 <= 0.) return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO); /* Set units */ s = 0; if ((name = pj_param(ctx, start, "sunits").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ; if (!s) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); s = pj_units[i].to_meter; } if (s || (s = pj_param(ctx, start, "sto_meter").s)) { double factor; int ratio = 0; /* ratio number? */ if (strlen (s) > 1 && s[0] == '1' && s[1]=='/') { ratio = 1; s += 2; } factor = pj_strtod(s, &s); if ((factor <= 0.0) || (1/factor==0)) return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); PIN->to_meter = ratio? 1 / factor: factor; PIN->fr_meter = 1 / PIN->to_meter; } else PIN->to_meter = PIN->fr_meter = 1.; /* Set vertical units */ s = 0; if ((name = pj_param(ctx, start, "svunits").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ; if (!s) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); s = pj_units[i].to_meter; } if (s || (s = pj_param(ctx, start, "svto_meter").s)) { PIN->vto_meter = pj_strtod(s, &s); if (*s == '/') /* ratio number */ PIN->vto_meter /= pj_strtod(++s, 0); if (PIN->vto_meter <= 0.0) return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); PIN->vfr_meter = 1. / PIN->vto_meter; } else { PIN->vto_meter = PIN->to_meter; PIN->vfr_meter = PIN->fr_meter; } /* Prime meridian */ s = 0; if ((name = pj_param(ctx, start, "spm").s) != NULL) { 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_ctx(ctx,name,&next_str) != 0.0 || *name == '0') && *next_str == '\0' ) value = name; if (!value) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_PRIME_MERIDIAN); PIN->from_greenwich = dmstor_ctx(ctx,value,NULL); } else PIN->from_greenwich = 0.0; /* Private object for the geodesic functions */ PIN->geod = pj_calloc (1, sizeof (struct geod_geodesic)); if (0==PIN->geod) return pj_default_destructor (PIN, ENOMEM); geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es))); /* Projection specific initialization */ err = proj_errno_reset (PIN); PIN = proj(PIN); if (proj_errno (PIN)) { pj_free(PIN); return 0; } proj_errno_restore (PIN, err); return PIN; }
static paralist *append_defaults_to_paralist (PJ_CONTEXT *ctx, paralist *start, char *key) { paralist *defaults, *last = 0; char keystring[ID_TAG_MAX + 20]; paralist *next, *proj; int err; if (0==start) return 0; if (strlen(key) > ID_TAG_MAX) return 0; /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */ /* or implicitly, because we are initializing a pipeline) */ if (pj_param_exists (start, "no_defs")) return start; proj = pj_param_exists (start, "proj"); if (0==proj) return start; if (strlen (proj->param) < 6) return start; if (0==strcmp ("pipeline", proj->param + 5)) return start; err = pj_ctx_get_errno (ctx); pj_ctx_set_errno (ctx, 0); /* Locate end of start-list */ for (last = start; last->next; last = last->next); strcpy (keystring, "proj_def.dat:"); strcat (keystring, key); defaults = get_init (ctx, keystring); /* Defaults are optional - so we don't care if we cannot open the file */ pj_ctx_set_errno (ctx, err); if (!defaults) return last; /* Loop over all default items */ for (next = defaults; next; next = next->next) { /* Don't override existing parameter value of same name */ if (pj_param_exists (start, next->param)) continue; /* Don't default ellipse if datum, ellps or any ellipsoid information is set */ if (0==strncmp(next->param,"ellps=", 6)) { if (pj_param_exists (start, "datum")) continue; if (pj_param_exists (start, "ellps")) continue; if (pj_param_exists (start, "a")) continue; if (pj_param_exists (start, "b")) continue; if (pj_param_exists (start, "rf")) continue; if (pj_param_exists (start, "f")) continue; if (pj_param_exists (start, "e")) continue; if (pj_param_exists (start, "es")) continue; } /* If we're here, it's OK to append the current default item */ last = last->next = pj_mkparam(next->param); } last->next = 0; pj_dealloc_params (ctx, defaults, 0); return last; }