diff options
| -rw-r--r-- | src/PJ_helmert.c | 32 | ||||
| -rw-r--r-- | src/gie.c | 47 | ||||
| -rw-r--r-- | src/pj_fwd.c | 115 | ||||
| -rw-r--r-- | src/pj_geocent.c | 2 | ||||
| -rw-r--r-- | src/pj_init.c | 23 | ||||
| -rw-r--r-- | src/pj_inv.c | 123 | ||||
| -rw-r--r-- | src/pj_malloc.c | 34 | ||||
| -rw-r--r-- | src/proj.def | 2 | ||||
| -rw-r--r-- | src/proj_4D_api.c | 119 | ||||
| -rw-r--r-- | src/proj_internal.h | 4 | ||||
| -rw-r--r-- | src/projects.h | 10 | ||||
| -rw-r--r-- | test/gie/4D-API_cs2cs-style.gie | 161 | ||||
| -rw-r--r-- | test/gie/axisswap.gie | 11 | ||||
| -rw-r--r-- | test/gie/builtins.gie | 16 | ||||
| -rw-r--r-- | test/gie/more_builtins.gie | 10 |
15 files changed, 603 insertions, 106 deletions
diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c index f726d946..229e30c2 100644 --- a/src/PJ_helmert.c +++ b/src/PJ_helmert.c @@ -486,6 +486,25 @@ PJ *TRANSFORMATION(helmert, 0) { P->right = PJ_IO_UNITS_PROJECTED; } + /* Support the classic PROJ towgs84 parameter, but allow later overrides.*/ + /* Note that if towgs84 is specified, the datum_params array is set up */ + /* for us automagically by the pj_datum_set call in pj_init_ctx */ + if (pj_param_exists (P->params, "towgs84")) { + Q->xyz_0.x = P->datum_params[0]; + Q->xyz_0.y = P->datum_params[1]; + Q->xyz_0.z = P->datum_params[2]; + + Q->opk_0.o = P->datum_params[3]; + Q->opk_0.p = P->datum_params[4]; + Q->opk_0.k = P->datum_params[5]; + + /* We must undo conversion to absolute scale from pj_datum_set */ + if (0==P->datum_params[6]) + Q->scale_0 = 0; + else + Q->scale_0 = (P->datum_params[6] - 1) * 1e6; + } + /* Translations */ if (pj_param (P->ctx, P->params, "tx").i) Q->xyz_0.x = pj_param (P->ctx, P->params, "dx").f; @@ -570,14 +589,13 @@ PJ *TRANSFORMATION(helmert, 0) { /* Let's help with debugging */ if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { proj_log_debug(P, "Helmert parameters:"); - proj_log_debug(P, "x= % 3.5f y= % 3.5f z= % 3.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z); - proj_log_debug(P, "rx= % 3.5f ry= % 3.5f rz= % 3.5f", + proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z); + proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); - proj_log_debug(P, "s=% 3.5f exact=% d transpose=% d", - Q->scale, Q->exact, Q->transpose); - proj_log_debug(P, "dx= % 3.5f dy= % 3.5f dz= % 3.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z); - proj_log_debug(P, "drx=% 3.5f dry=% 3.5f drz=% 3.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k); - proj_log_debug(P, "ds=% 3.5f t_epoch=% 5.5f t_obs=% 5.5f", Q->dscale, Q->t_epoch, Q->t_obs); + proj_log_debug(P, "s= %8.5f exact=%d transpose=%d", Q->scale, Q->exact, Q->transpose); + proj_log_debug(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z); + proj_log_debug(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k); + proj_log_debug(P, "ds= %8.5f t_epoch=%8.5f t_obs=%8.5f", Q->dscale, Q->t_epoch, Q->t_obs); } if ((Q->opk.o==0) && (Q->opk.p==0) && (Q->opk.k==0) && (Q->scale==0) && @@ -387,6 +387,7 @@ Interpret <args> as a numeric followed by a linear decadal prefix. Return the properly scaled numeric ******************************************************************************/ double s; + const double GRS80_DEG = 111319.4908; /* deg-to-m at equator of GRS80 */ const char *endp = args; s = proj_strtod (args, (char **) &endp); if (args==endp) @@ -408,6 +409,10 @@ Return the properly scaled numeric s /= 1e6; else if (0==strcmp(endp, "nm")) s /= 1e9; + else if (0==strcmp(endp, "rad")) + s = GRS80_DEG * proj_todeg (s); + else if (0==strcmp(endp, "deg")) + s = GRS80_DEG * s; else s *= default_scale; return s; @@ -553,19 +558,29 @@ using the "builtins" command verb. } -static PJ_COORD torad_coord (PJ_COORD a) { - PJ_COORD c = a; - c.lpz.lam = proj_torad (a.lpz.lam); - c.lpz.phi = proj_torad (a.lpz.phi); - return c; +static PJ_COORD torad_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { + size_t i, n; + char *axis = "enut"; + paralist *l = pj_param_exists (P->params, "axis"); + if (l && dir==PJ_INV) + axis = l->param + strlen ("axis="); + for (i = 0, n = strlen (axis); i < n; i++) + if (strchr ("news", axis[i])) + a.v[i] = proj_torad (a.v[i]); + return a; } -static PJ_COORD todeg_coord (PJ_COORD a) { - PJ_COORD c = a; - c.lpz.lam = proj_todeg (a.lpz.lam); - c.lpz.phi = proj_todeg (a.lpz.phi); - return c; +static PJ_COORD todeg_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { + size_t i, n; + char *axis = "enut"; + paralist *l = pj_param_exists (P->params, "axis"); + if (l && dir==PJ_FWD) + axis = l->param + strlen ("axis="); + for (i = 0, n = strlen (axis); i < n; i++) + if (strchr ("news", axis[i])) + a.v[i] = proj_todeg (a.v[i]); + return a; } @@ -627,7 +642,7 @@ back/forward transformation pairs. d = d==HUGE_VAL? T.tolerance: d; /* input ("accepted") values - probably in degrees */ - coo = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a; + coo = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; r = proj_roundtrip (T.P, T.dir, ntrips, &coo); if (r <= d) @@ -747,7 +762,7 @@ Tell GIE what to expect, when transforming the ACCEPTed input proj_errno_reset (T.P); /* Try to carry out the operation - and expect failure */ - ci = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a; + ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; co = expect_trans_n_dim (ci); /* Failed to fail? - that's a failure */ @@ -780,20 +795,20 @@ Tell GIE what to expect, when transforming the ACCEPTed input return expect_message_cannot_parse (args); /* expected angular values, probably in degrees */ - ce = proj_angular_output (T.P, T.dir)? torad_coord (T.e): T.e; + ce = proj_angular_output (T.P, T.dir)? torad_coord (T.P, T.dir, T.e): T.e; if (T.verbosity > 3) printf ("EXPECTS %.4f %.4f %.4f %.4f\n", ce.v[0],ce.v[1],ce.v[2],ce.v[3]); /* input ("accepted") values, also probably in degrees */ - ci = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a; + ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; if (T.verbosity > 3) printf ("ACCEPTS %.4f %.4f %.4f %.4f\n", ci.v[0],ci.v[1],ci.v[2],ci.v[3]); /* angular output from proj_trans comes in radians */ co = expect_trans_n_dim (ci); - T.b = proj_angular_output (T.P, T.dir)? todeg_coord (co): co; + T.b = proj_angular_output (T.P, T.dir)? todeg_coord (T.P, T.dir, co): co; if (T.verbosity > 3) - printf ("GOT %.4f %.4f %.4f %.4f\n", ci.v[0],ci.v[1],ci.v[2],ci.v[3]); + printf ("GOT %.4f %.4f %.4f %.4f\n", co.v[0],co.v[1],co.v[2],co.v[3]); /* but there are a few more possible input conventions... */ if (proj_angular_output (T.P, T.dir)) { diff --git a/src/pj_fwd.c b/src/pj_fwd.c index a1025aac..2293cf2e 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -32,58 +32,137 @@ #include "proj_internal.h" #include "projects.h" -PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { +#define INPUT_UNITS P->left +#define OUTPUT_UNITS P->right + + +static PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { if (HUGE_VAL==coo.v[0]) return proj_coord_error (); + /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ + if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; + if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; + /* Check validity of angular input coordinates */ - if (P->left==PJ_IO_UNITS_RADIANS) { + if (INPUT_UNITS==PJ_IO_UNITS_RADIANS) { double t; - LP lp = coo.lp; /* check for latitude or longitude over-range */ - t = (lp.phi < 0 ? -lp.phi : lp.phi) - M_HALFPI; - if (t > PJ_EPS_LAT || lp.lam > 10 || lp.lam < -10) { + t = (coo.lp.phi < 0 ? -coo.lp.phi : coo.lp.phi) - M_HALFPI; + if (t > PJ_EPS_LAT || coo.lp.lam > 10 || coo.lp.lam < -10) { proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); return proj_coord_error (); } /* Clamp latitude to -90..90 degree range */ - if (lp.phi > M_HALFPI) - lp.phi = M_HALFPI; - if (lp.phi < -M_HALFPI) - lp.phi = -M_HALFPI; + if (coo.lp.phi > M_HALFPI) + coo.lp.phi = M_HALFPI; + if (coo.lp.phi < -M_HALFPI) + coo.lp.phi = -M_HALFPI; /* If input latitude is geocentrical, convert to geographical */ if (P->geoc) coo = proj_geoc_lat (P, PJ_INV, coo); + /* Ensure longitude is in the -pi:pi range */ + if (0==P->over) + coo.lp.lam = adjlon(coo.lp.lam); + + if (P->hgridshift) + coo = proj_trans (P->hgridshift, PJ_INV, coo); + else if (P->helmert) { + coo = proj_trans (P->cart_wgs84, PJ_FWD, coo); /* Go cartesian in WGS84 frame */ + coo = proj_trans (P->helmert, PJ_FWD, coo); /* Step into local frame */ + coo = proj_trans (P->cart, PJ_INV, coo); /* Go back to angular using local ellps */ + } + if (P->vgridshift) + coo = proj_trans (P->vgridshift, PJ_FWD, coo); + /* Distance from central meridian, taking system zero meridian into account */ - lp.lam = (lp.lam - P->from_greenwich) - P->lam0; + coo.lp.lam = (coo.lp.lam - P->from_greenwich) - P->lam0; /* Ensure longitude is in the -pi:pi range */ if (0==P->over) - lp.lam = adjlon(lp.lam); + coo.lp.lam = adjlon(coo.lp.lam); - coo.lp = lp; + return coo; } + + /* We do not support gridshifts on cartesian input */ + if (INPUT_UNITS==PJ_IO_UNITS_CARTESIAN && P->helmert) + return proj_trans (P->helmert, PJ_FWD, coo); return coo; } -PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { + +static PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { + + switch (OUTPUT_UNITS) { + + /* Handle false eastings/northings and non-metric linear units */ + case PJ_IO_UNITS_CARTESIAN: + + if (P->is_geocent) { + coo = proj_trans (P->cart, PJ_FWD, coo); + } + + coo.xyz.x = P->fr_meter * (coo.xyz.x + P->x0); + coo.xyz.y = P->fr_meter * (coo.xyz.y + P->y0); + coo.xyz.z = P->fr_meter * (coo.xyz.z + P->z0); + break; /* Classic proj.4 functions return plane coordinates in units of the semimajor axis */ - if (P->right==PJ_IO_UNITS_CLASSIC) { + case PJ_IO_UNITS_CLASSIC: coo.xy.x *= P->a; coo.xy.y *= P->a; + + /* Falls through */ /* (<-- GCC warning silencer) */ + /* to continue processing in common with PJ_IO_UNITS_PROJECTED */ + case PJ_IO_UNITS_PROJECTED: + coo.xyz.x = P->fr_meter * (coo.xyz.x + P->x0); + coo.xyz.y = P->fr_meter * (coo.xyz.y + P->y0); + coo.xyz.z = P->vfr_meter * (coo.xyz.z + P->z0); + break; + + case PJ_IO_UNITS_WHATEVER: + break; + + case PJ_IO_UNITS_RADIANS: + if (INPUT_UNITS==PJ_IO_UNITS_RADIANS) + break; + + /* adjust longitude to central meridian */ + if (0==P->over) + coo.lpz.lam = adjlon(coo.lpz.lam); + + if (P->vgridshift) + coo = proj_trans (P->vgridshift, PJ_INV, coo); + if (P->hgridshift) + coo = proj_trans (P->hgridshift, PJ_FWD, coo); + else if (P->helmert) { + coo = proj_trans (P->cart_wgs84, PJ_FWD, coo); /* Go cartesian in WGS84 frame */ + coo = proj_trans (P->helmert, PJ_FWD, coo); /* Step into local frame */ + coo = proj_trans (P->cart, PJ_INV, coo); /* Go back to angular using local ellps */ + } + + /* If input latitude was geocentrical, convert back to geocentrical */ + if (P->geoc) + coo = proj_geoc_lat (P, PJ_FWD, coo); + + + /* Distance from central meridian, taking system zero meridian into account */ + coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; + + /* adjust longitude to central meridian */ + if (0==P->over) + coo.lpz.lam = adjlon(coo.lpz.lam); } - /* Handle false eastings/northings and non-metric linear units */ - coo.xyz.x = P->fr_meter * (coo.xyz.x + P->x0); - coo.xyz.y = P->fr_meter * (coo.xyz.y + P->y0); - coo.xyz.z = P->vfr_meter * (coo.xyz.z + P->z0); + if (P->axisswap) + coo = proj_trans (P->axisswap, PJ_FWD, coo); return coo; } diff --git a/src/pj_geocent.c b/src/pj_geocent.c index eca62080..e676b735 100644 --- a/src/pj_geocent.c +++ b/src/pj_geocent.c @@ -54,6 +54,8 @@ PJ *PROJECTION(geocent) { P->y0 = 0.0; P->inv = inverse; P->fwd = forward; + P->left = PJ_IO_UNITS_RADIANS; + P->right = PJ_IO_UNITS_CARTESIAN; return P; } diff --git a/src/pj_init.c b/src/pj_init.c index f25c0344..51a742c7 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -769,26 +769,3 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { proj_errno_restore (PIN, err); 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->destructor(), to do local work. */ -/* In most cases P->destructor()==pj_default_destructor. */ -/************************************************************************/ - -void pj_free(PJ *P) { - if (0==P) - return; - /* free projection parameters - all the hard work is done by */ - /* pj_default_destructor (in pj_malloc.c), which is supposed */ - /* to be called as the last step of the local destructor */ - /* pointed to by P->destructor. In most cases, */ - /* pj_default_destructor actually *is* what is pointed to */ - P->destructor (P, 0); -} diff --git a/src/pj_inv.c b/src/pj_inv.c index e1fb05f6..02a2ca53 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -32,46 +32,131 @@ #include "proj_internal.h" #include "projects.h" +#define INPUT_UNITS P->right +#define OUTPUT_UNITS P->left - -PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { +static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { if (coo.xyz.x == HUGE_VAL) { proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); return proj_coord_error (); } + /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ + if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; + if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; + + if (P->axisswap) + coo = proj_trans (P->axisswap, PJ_INV, coo); + + /* Check validity of angular input coordinates */ + if (INPUT_UNITS==PJ_IO_UNITS_RADIANS) { + double t; + + /* check for latitude or longitude over-range */ + t = (coo.lp.phi < 0 ? -coo.lp.phi : coo.lp.phi) - M_HALFPI; + if (t > PJ_EPS_LAT || coo.lp.lam > 10 || coo.lp.lam < -10) { + proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + return proj_coord_error (); + } + + /* Clamp latitude to -90..90 degree range */ + if (coo.lp.phi > M_HALFPI) + coo.lp.phi = M_HALFPI; + if (coo.lp.phi < -M_HALFPI) + coo.lp.phi = -M_HALFPI; + + /* If input latitude is geocentrical, convert to geographical */ + if (P->geoc) + coo = proj_geoc_lat (P, PJ_INV, coo); + + /* Distance from central meridian, taking system zero meridian into account */ + coo.lp.lam = (coo.lp.lam + P->from_greenwich) - P->lam0; + + /* Ensure longitude is in the -pi:pi range */ + if (0==P->over) + coo.lp.lam = adjlon(coo.lp.lam); + + if (P->hgridshift) + coo = proj_trans (P->hgridshift, PJ_FWD, coo); + else if (P->helmert) { + coo = proj_trans (P->cart, PJ_FWD, coo); /* Go cartesian in local frame */ + coo = proj_trans (P->helmert, PJ_INV, coo); /* Step into WGS84 */ + coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ + } + if (P->vgridshift) + coo = proj_trans (P->vgridshift, PJ_INV, coo); + return coo; + } + + /* Handle remaining possible input types */ + switch (INPUT_UNITS) { + case PJ_IO_UNITS_WHATEVER: + return coo; + /* de-scale and de-offset */ - coo.xyz.x = (coo.xyz.x * P->to_meter - P->x0); - coo.xyz.y = (coo.xyz.y * P->to_meter - P->y0); - coo.xyz.z = (coo.xyz.z * P->vto_meter - P->z0); - - /* Classic proj.4 functions expect plane coordinates in units of the semimajor axis */ - /* Multiplying by ra, rather than dividing by a because the CALCOFI projection */ - /* stomps on a and hence (apparently) depends on this to roundtrip correctly */ - /* (CALCOFI avoids further scaling by stomping - a better solution must be possible) */ - if (P->right==PJ_IO_UNITS_CLASSIC) { + case PJ_IO_UNITS_CARTESIAN: + coo.xyz.x = P->to_meter * coo.xyz.x - P->x0; + coo.xyz.y = P->to_meter * coo.xyz.y - P->y0; + coo.xyz.z = P->to_meter * coo.xyz.z - P->z0; + + if (P->is_geocent) { + coo = proj_trans (P->cart, PJ_INV, coo); + } + + return coo; + + case PJ_IO_UNITS_PROJECTED: + case PJ_IO_UNITS_CLASSIC: + coo.xyz.x = P->to_meter * coo.xyz.x - P->x0; + coo.xyz.y = P->to_meter * coo.xyz.y - P->y0; + coo.xyz.z = P->vto_meter * coo.xyz.z - P->z0; + if (INPUT_UNITS==PJ_IO_UNITS_PROJECTED) + return coo; + + /* Classic proj.4 functions expect plane coordinates in units of the semimajor axis */ + /* Multiplying by ra, rather than dividing by a because the CALCOFI projection */ + /* stomps on a and hence (apparently) depends on this to roundtrip correctly */ + /* (CALCOFI avoids further scaling by stomping - but a better solution is possible) */ coo.xyz.x *= P->ra; coo.xyz.y *= P->ra; + return coo; + /* Silence some compiler warnings about PJ_IO_UNITS_RADIANS not handled */ + default: + break; } + /* Should not happen, so we could return pj_coord_err here */ return coo; } -PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo) { +static PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo) { if (coo.xyz.x == HUGE_VAL) { proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); return proj_coord_error (); } - if (P->left==PJ_IO_UNITS_RADIANS) { - /* Distance from central meridian, taking system zero meridian into account */ - coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; - - /* adjust longitude to central meridian */ - if (0==P->over) - coo.lpz.lam = adjlon(coo.lpz.lam); + if (OUTPUT_UNITS==PJ_IO_UNITS_RADIANS) { + + if (INPUT_UNITS!=PJ_IO_UNITS_RADIANS) { + /* Distance from central meridian, taking system zero meridian into account */ + coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; + + /* adjust longitude to central meridian */ + if (0==P->over) + coo.lpz.lam = adjlon(coo.lpz.lam); + + if (P->vgridshift) + coo = proj_trans (P->vgridshift, PJ_INV, coo); + if (P->hgridshift) + coo = proj_trans (P->hgridshift, PJ_FWD, coo); + else if (P->helmert) { + coo = proj_trans (P->cart, PJ_FWD, coo); /* Go cartesian in local frame */ + coo = proj_trans (P->helmert, PJ_INV, coo); /* Step into WGS84 */ + coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ + } + } /* If input latitude was geocentrical, convert back to geocentrical */ if (P->geoc) diff --git a/src/pj_malloc.c b/src/pj_malloc.c index 63278a56..cfb7f658 100644 --- a/src/pj_malloc.c +++ b/src/pj_malloc.c @@ -162,6 +162,32 @@ void *pj_dealloc_params (PJ_CONTEXT *ctx, paralist *start, int errlev) { } + + +/************************************************************************/ +/* 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->destructor(), to do local work. */ +/* In most cases P->destructor()==pj_default_destructor. */ +/************************************************************************/ + +void pj_free(PJ *P) { + if (0==P) + return; + /* free projection parameters - all the hard work is done by */ + /* pj_default_destructor, which is supposed */ + /* to be called as the last step of the local destructor */ + /* pointed to by P->destructor. In most cases, */ + /* pj_default_destructor actually *is* what is pointed to */ + P->destructor (P, 0); +} + + + + /*****************************************************************************/ void *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ /***************************************************************************** @@ -196,6 +222,14 @@ void *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ /* free parameter list elements */ pj_dealloc_params (pj_get_ctx(P), P->params, errlev); + /* free the cs2cs emulation elements */ + pj_free (P->axisswap); + pj_free (P->helmert); + pj_free (P->cart); + pj_free (P->cart_wgs84); + pj_free (P->hgridshift); + pj_free (P->vgridshift); + pj_dealloc (P->opaque); return pj_dealloc(P); } diff --git a/src/proj.def b/src/proj.def index 6ab0c007..099a78a5 100644 --- a/src/proj.def +++ b/src/proj.def @@ -155,3 +155,5 @@ EXPORTS pj_approx_2D_trans @138 pj_approx_3D_trans @139 pj_has_inverse @140 + pj_param_exists @141 + diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 2c6bac2d..fa24c430 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -380,7 +380,114 @@ char* proj_rtodms(char *s, double r, int pos, int neg) { return rtodms(s, r, pos, neg); } +/*************************************************************************************/ +static PJ* skip_prep_fin(PJ *P) { +/************************************************************************************** +Skip prepare and finalize function for the various "helper operations" added to P when +in cs2cs compatibility mode. +**************************************************************************************/ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + return P; +} + +/*************************************************************************************/ +static int pj_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. +**************************************************************************************/ + PJ *Q; + paralist *p; + char def[1000]; + 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))) { + sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); + Q = proj_create (P->ctx, 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="); + sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames); + Q = proj_create (P->ctx, 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="); + sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames); + Q = proj_create (P->ctx, 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 *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]) + break; + + if (n > 900) + return 0; + if (n <= 8) /* 8==strlen ("towgs84=") */ + return 0; + sprintf (def, "break_cs2cs_recursion proj=helmert %s", s); + Q = proj_create (P->ctx, 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) { + char *wgs84 = "ellps=WGS84"; + sprintf (def, "break_cs2cs_recursion proj=cart"); + Q = proj_create (P->ctx, def); + if (0==Q) + return 0; + pj_inherit_ellipsoid_def(P, Q); + P->cart = skip_prep_fin(Q); + + sprintf (def, "break_cs2cs_recursion proj=cart %s", wgs84); + Q = proj_create (P->ctx, def); + if (0==Q) + return 0; + P->cart_wgs84 = skip_prep_fin(Q); + } + return 1; +} /*************************************************************************************/ @@ -394,9 +501,10 @@ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { It may even use free formatting "proj = utm; zone =32 ellps= GRS80". Note that the semicolon separator is allowed, but not required. **************************************************************************************/ - PJ *P; - char *args, **argv; + PJ *P; + char *args, **argv; size_t argc, n; + int ret; if (0==ctx) ctx = pj_get_default_ctx (); @@ -421,6 +529,12 @@ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { pj_dealloc (argv); pj_dealloc (args); + + /* Support cs2cs-style modifiers */ + ret = pj_cs2cs_emulation_setup (P); + if (0==ret) + return proj_destroy (P); + return P; } @@ -449,6 +563,7 @@ indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm", return 0; P = proj_create (ctx, c); + pj_dealloc ((char *) c); return P; } diff --git a/src/proj_internal.h b/src/proj_internal.h index 41f95c07..2c6cbee2 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -84,10 +84,6 @@ void proj_context_inherit (PJ *parent, PJ *child); PJ_COORD pj_fwd4d (PJ_COORD coo, PJ *P); PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P); -PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo); -PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo); -PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo); -PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo); PJ_COORD pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo); PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo); diff --git a/src/projects.h b/src/projects.h index 86ca2524..aeaf09ae 100644 --- a/src/projects.h +++ b/src/projects.h @@ -349,6 +349,14 @@ struct PJconsts { enum pj_io_units left; /* Flags for input/output coordinate types */ enum pj_io_units right; + /* These PJs are used for implementing cs2cs style coordinate handling in the 4D API */ + PJ *axisswap; + PJ *cart; + PJ *cart_wgs84; + PJ *helmert; + PJ *hgridshift; + PJ *vgridshift; + /************************************************************************************* @@ -394,7 +402,7 @@ struct PJconsts { double from_greenwich; /* prime meridian offset (in radians) */ double long_wrap_center; /* 0.0 for -180 to 180, actually in radians*/ int is_long_wrap_set; - char axis[4]; /* TODO: Description needed */ + char axis[4]; /* Axis order, pj_transform/pj_adjust_axis */ /* New Datum Shift Grid Catalogs */ char *catalog_name; diff --git a/test/gie/4D-API_cs2cs-style.gie b/test/gie/4D-API_cs2cs-style.gie new file mode 100644 index 00000000..853df993 --- /dev/null +++ b/test/gie/4D-API_cs2cs-style.gie @@ -0,0 +1,161 @@ +=============================================================================== + +Test the 4D API handling of cs2cs style transformation options. + +These tests are mostly based on the same material as those in +more_builtins.gie, since we are testing the same kinds of things, +but provided through a different interface. + +=============================================================================== + + +<gie> + +------------------------------------------------------------------------------- +Test the handling of the +towgs84 parameter +------------------------------------------------------------------------------- +This example is from Lotti Jivall: "Simplified transformations from +ITRF2008/IGS08 to ETRS89 for maritime applications". The original +XYZ data (cf. more_builtins.gie) have been transformed to LPZ using +this command: echo x_val y_val z_val | cct -It0 proj=cart ellps=GRS80 -- + +NOTE: Here, the ellipsoid has been swapped to WGS84, to align with + the WGS84 ellipsoid used in the cs2cs emulation introduced by + pj_cs2cs_emulation_setup() +------------------------------------------------------------------------------- +operation proj=latlong ellps=WGS84 + towgs84 = 0.676780, 0.654950, -0.528270, + -0.022742, 0.012667, 0.022704, -0.010700 +------------------------------------------------------------------------------- +tolerance 0.05 mm +direction forward + +accept 13.4999969828397 54.9999995493206 -0.6034 +expect 13.4999906103972 54.9999957469562 -0.6374 + +direction inverse + +accept 13.4999906103972 54.9999957469562 -0.6374 +expect 13.4999969828397 54.9999995493206 -0.6034 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +This example is a random point, transformed from ED50 to ETRS89 using KMStrans2. + +NOTE: Signs swapped wrt. KMStrans2, which apparently uses frame rotation. +------------------------------------------------------------------------------- +operation proj=latlong ellps=intl + towgs84 = 081.07030, 089.36030, 115.75260, + 000.48488, 000.02436, 000.41321, 0.540645 +------------------------------------------------------------------------------- +tolerance 0.1 mm +# echo SWAPPED and trimmed - expect succcess +# accept 16.819999997 55.170000002 61.0 +accept 16.82 55.17 61.0 +expect 16.8210462130 55.1705688946 29.0317 +------------------------------------------------------------------------------- +This commented-out version of the example above was used to detect the sign +discrepancy between KMSTrans2 and PROJ. +------------------------------------------------------------------------------- +operation proj=latlong ellps=intl + towgs84 = -081.07030, -089.36030, -115.75260, + -000.48488, -000.02436, -000.41321, -0.540645 +------------------------------------------------------------------------------- +# echo NOT SWAPPED - Expect failure +# accept 16.8210462130 55.1705688946 29.0317 +# expect 16.819999997 55.170000002 61.0 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +operation proj=latlong nadgrids=nzgd2kgrid0005.gsb ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 1 nm +accept 173 -45 0 0 +expect 172.999892181021551 -45.001620431954613 0 0 +direction inverse +accept 172.999892181021551 -45.001620431954613 0 0 +expect 173 -45 0 0 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widespread bad egm96 file +accept 12.5 55.5 0 +expect 12.5 55.5 -36.0213 +direction inverse +accept 12.5 55.5 -36.0213 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=merc geoidgrids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 12.5 55.5 0 +expect 1391493.63492 7424275.19462 -36.0213 +direction inverse +accept 1391493.63492 7424275.19462 -36.0213 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +Same as the two above, but also do axis swapping. +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx axis=neu ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widely distributed, bad egm96 file +accept 12.5 55.5 0 +expect 55.5 12.5 -36.0213 +direction inverse +accept 55.5 12.5 -36.0213 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx axis=dne ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widely distributed, bad egm96 file +accept 12.5 55.5 0 +expect 36.0213 55.5 12.5 +direction inverse +accept 36.0213 55.5 12.5 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=merc geoidgrids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 12.5 55.5 0 +expect 1391493.63492 7424275.19462 -36.0213 +direction inverse +accept 1391493.63492 7424275.19462 -36.0213 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +Some more complex axis swapping. +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx axis=nue ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widely distributed, bad egm96 file +accept 12.5 55.5 0 +expect 55.5 -36.0213 12.5 +direction inverse +accept 55.5 -36.0213 12.5 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=merc geoidgrids=egm96_15.gtx axis=sue ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm +accept 12.5 55.5 0 +expect -7424275.1946 -36.0213 1391493.6349 0.0000 +direction inverse +accept -7424275.1946 -36.0213 1391493.6349 0.0000 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- +</gie> diff --git a/test/gie/axisswap.gie b/test/gie/axisswap.gie index ac148a92..190ca3e7 100644 --- a/test/gie/axisswap.gie +++ b/test/gie/axisswap.gie @@ -53,9 +53,14 @@ expect 3 -2 1 4 roundtrip 100 operation proj=axisswap axis=neu -tolerance 0.000001 m -accept 1 2 3 4 -expect 2 1 3 4 +tolerance 0 m +accept 1 2 3 +expect 2 1 3 + +operation proj=axisswap axis=nue +tolerance 0 m +accept 1 2 3 +expect 2 3 1 operation proj=axisswap axis=swd tolerance 0.000001 m diff --git a/test/gie/builtins.gie b/test/gie/builtins.gie index 3877b58a..e8f1bfe1 100644 --- a/test/gie/builtins.gie +++ b/test/gie/builtins.gie @@ -1233,14 +1233,14 @@ Geocentric operation +proj=geocent +ellps=GRS80 +lat_1=0.5 +lat_2=2 ------------------------------------------------------------------------------- tolerance 0.00010 mm -accept 2 1 -expect 222638.981586547 111319.490793274 -accept 2 -1 -expect 222638.981586547 -111319.490793274 -accept -2 1 -expect -222638.981586547 111319.490793274 -accept -2 -1 -expect -222638.981586547 -111319.490793274 +accept 2 1 0 +expect 6373287.27950247 222560.09599219 110568.77482092 +accept 2 -1 0 +expect 6373287.27950247 222560.09599219 -110568.77482092 +accept -2 1 0 +expect 6373287.27950247 -222560.09599219 110568.77482092 +accept -2 -1 0 +expect 6373287.27950247 -222560.09599219 -110568.77482092 direction inverse accept 200 100 diff --git a/test/gie/more_builtins.gie b/test/gie/more_builtins.gie index 20bffbd9..e21f0321 100644 --- a/test/gie/more_builtins.gie +++ b/test/gie/more_builtins.gie @@ -131,11 +131,11 @@ expect 12 55 0 0 ------------------------------------------------------------------------------- Finally test a pipeline with more than one init step ------------------------------------------------------------------------------- -operation proj=pipeline step - init=epsg:25832 inv step - init=epsg:25833 step - init=epsg:25833 inv step - init=epsg:25832 +operation proj=pipeline + step init=epsg:25832 inv + step init=epsg:25833 + step init=epsg:25833 inv + step init=epsg:25832 ------------------------------------------------------------------------------- accept 691875.63214 6098907.82501 0 0 expect 691875.63214 6098907.82501 0 0 |
