From 3adb9f80135d08ad9e8d90609b034cc1c67a513b Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 6 Dec 2017 11:38:25 +0100 Subject: Use t_epoch and t_obs instead of epoch and tobs. Prefixing with t_ is more consistent with the existing parameters in PROJ, such as x_0 and friends. t_epoch and t_obs is already used in PJ_deformation. Now users can expect consistency across time-varying transformations. --- src/PJ_helmert.c | 16 ++++++++-------- src/gie.c | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c index 34bb7a68..1f36acee 100644 --- a/src/PJ_helmert.c +++ b/src/PJ_helmert.c @@ -73,7 +73,7 @@ struct pj_opaque_helmert { double theta_0; double dtheta; double R[3][3]; - double epoch, t_obs; + double t_epoch, t_obs; int no_rotation, approximate, transpose, fourparam; }; @@ -118,7 +118,7 @@ static void update_parameters(PJ *P) { *******************************************************************************/ struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - double dt = Q->t_obs - Q->epoch; + double dt = Q->t_obs - Q->t_epoch; Q->xyz.x = Q->xyz_0.x + Q->dxyz.x * dt; Q->xyz.y = Q->xyz_0.y + Q->dxyz.y * dt; @@ -134,7 +134,7 @@ static void update_parameters(PJ *P) { /* debugging output */ if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { - proj_log_trace(P, "Transformation parameters for observation epoch %g:", Q->t_obs); + proj_log_trace(P, "Transformation parameters for observation t_epoch %g:", Q->t_obs); proj_log_trace(P, "x: %g", Q->xyz.x); proj_log_trace(P, "y: %g", Q->xyz.y); proj_log_trace(P, "z: %g", Q->xyz.z); @@ -539,11 +539,11 @@ PJ *TRANSFORMATION(helmert, 0) { /* Epoch */ - if (pj_param(P->ctx, P->params, "tepoch").i) - Q->epoch = pj_param (P->ctx, P->params, "depoch").f; + if (pj_param(P->ctx, P->params, "tt_epoch").i) + Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; - if (pj_param(P->ctx, P->params, "ttobs").i) - Q->t_obs = pj_param (P->ctx, P->params, "dtobs").f; + if (pj_param(P->ctx, P->params, "tt_obs").i) + Q->t_obs = pj_param (P->ctx, P->params, "dt_obs").f; /* Use small angle approximations? */ if (pj_param (P->ctx, P->params, "bapprox").i) @@ -568,7 +568,7 @@ PJ *TRANSFORMATION(helmert, 0) { Q->scale, Q->approximate, 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 epoch=% 5.5f tobs=% 5.5f", Q->dscale, Q->epoch, Q->t_obs); + proj_log_debug(P, "ds=% 3.5f t_epoch=% 5.5f t_obs=% 5.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) && diff --git a/src/gie.c b/src/gie.c index 9e377648..fa7921dd 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1592,7 +1592,7 @@ static int pj_cart_selftest (void) { " +rx=-0.00039 +ry=0.00080 +rz=-0.00114" " +dx=-0.0029 +dy=-0.0002 +dz=-0.0006 +ds=0.00001" " +drx=-0.00011 +dry=-0.00019 +drz=0.00007" - " +epoch=1988.0 +transpose" + " +t_epoch=1988.0 +transpose" ); if (0==P) return 0; if (proj_angular_input (P, PJ_FWD)) return 116; -- cgit v1.2.3 From 2203189482646c208cc4c9d212909c5a7944bb1a Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 6 Dec 2017 14:42:51 +0100 Subject: Use approximate equations instead of exact as default in Helmert. --- src/PJ_helmert.c | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c index 1f36acee..ca14a5cb 100644 --- a/src/PJ_helmert.c +++ b/src/PJ_helmert.c @@ -74,7 +74,7 @@ struct pj_opaque_helmert { double dtheta; double R[3][3]; double t_epoch, t_obs; - int no_rotation, approximate, transpose, fourparam; + int no_rotation, exact, transpose, fourparam; }; @@ -160,7 +160,7 @@ static void build_rot_matrix(PJ *P) { at https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions The relevant section is Euler angles ( z-’-x" intrinsic) -> Rotation matrix - If the option "approximate" is set, small angle approximations are used: + By default small angle approximations are used: The matrix elements are approximated by expanding the trigonometric functions to linear order (i.e. cos(x) = 1, sin(x) = x), and discarding products of second order. @@ -179,8 +179,11 @@ static void build_rot_matrix(PJ *P) { However, in many cases the approximation is necessary, since it has been used historically: Rotation angles from older published datum shifts may actually be a least squares fit to the linearized rotation - approximation, hence not being strictly valid for deriving the full - rotation matrix. + approximation, hence not being strictly valid for deriving the exact + rotation matrix. In fact, most publicly available transformation + parameters are based on the approximate Helmert transform, which is why + we use that as the default setting, even though it is more correct to + use the exact form of the equations. So in order to fit historically derived coordinates, the access to the approximate rotation matrix is necessary - at least in principle. @@ -222,20 +225,7 @@ static void build_rot_matrix(PJ *P) { t = Q->opk.p; p = Q->opk.k; - if (Q->approximate) { - R00 = 1; - R01 = p; - R02 = -t; - - R10 = -p; - R11 = 1; - R12 = f; - - R20 = t; - R21 = -f; - R22 = 1; - } - else { + if (Q->exact) { cf = cos(f); sf = sin(f); ct = cos(t); @@ -255,8 +245,21 @@ static void build_rot_matrix(PJ *P) { R20 = st; R21 = -sf*ct; R22 = cf*ct; + } else{ + R00 = 1; + R01 = p; + R02 = -t; + + R10 = -p; + R11 = 1; + R12 = f; + + R20 = t; + R21 = -f; + R22 = 1; } + /* For comparison: Description from Engsager/Poder implementation in set_dtm_1.c (trlib) @@ -546,8 +549,8 @@ PJ *TRANSFORMATION(helmert, 0) { Q->t_obs = pj_param (P->ctx, P->params, "dt_obs").f; /* Use small angle approximations? */ - if (pj_param (P->ctx, P->params, "bapprox").i) - Q->approximate = 1; + if (pj_param (P->ctx, P->params, "bexact").i) + Q->exact = 1; /* Use "other" rotation sign convention? */ if (pj_param (P->ctx, P->params, "ttranspose").i) @@ -564,8 +567,8 @@ PJ *TRANSFORMATION(helmert, 0) { 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", 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 approximate=% d transpose=% d", - Q->scale, Q->approximate, Q->transpose); + 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); -- cgit v1.2.3 From 0bc0e045e4352feb3acc374c779e66c79fcade75 Mon Sep 17 00:00:00 2001 From: Aaron Puchert Date: Mon, 11 Dec 2017 17:53:06 +0100 Subject: Allow including the new API header in proj.c This didn't work before because the elaborate type punning scheme was deactivated by including proj.h. Now we use the type punning from the new API header and build our own. This change is required for #674. --- src/proj.c | 118 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 61 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/proj.c b/src/proj.c index 77d6b28b..7922a5ac 100644 --- a/src/proj.c +++ b/src/proj.c @@ -1,4 +1,5 @@ /* <<<< Cartographic projection filter program >>>> */ +#include "proj.h" #include "projects.h" #include #include @@ -10,7 +11,7 @@ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__) # include # include -# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +# define SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif @@ -22,7 +23,11 @@ extern void gen_cheb(int, projUV(*)(projUV), char *, PJ *, int, char **); static PJ *Proj; -static projUV (*proj)(projUV, PJ *); +static union { + projUV (*generic)(projUV, PJ *); + projXY (*fwd)(projLP, PJ *); + projLP (*inv)(projXY, PJ *); +} proj; static int reversein = 0, /* != 0 reverse input arguments */ @@ -55,7 +60,7 @@ static projUV int_proj(projUV data) { data.v *= fscale; } - data = (*proj)(data, Proj); + data = (*proj.generic)(data, Proj); if (postscale && data.u != HUGE_VAL) { data.u *= fscale; @@ -68,7 +73,7 @@ static projUV int_proj(projUV data) { /* file processing function */ static void process(FILE *fid) { char line[MAX_LINE+3], *s = 0, pline[40]; - projUV data; + PJ_COORD data; for (;;) { ++emess_dat.File_line; @@ -94,15 +99,15 @@ static void process(FILE *fid) { } if (reversein) { - data.v = (*informat)(s, &s); - data.u = (*informat)(s, &s); + data.uv.v = (*informat)(s, &s); + data.uv.u = (*informat)(s, &s); } else { - data.u = (*informat)(s, &s); - data.v = (*informat)(s, &s); + data.uv.u = (*informat)(s, &s); + data.uv.v = (*informat)(s, &s); } - if (data.v == HUGE_VAL) - data.u = HUGE_VAL; + if (data.uv.v == HUGE_VAL) + data.uv.u = HUGE_VAL; if (!*s && (s > line)) --s; /* assumed we gobbled \n */ if (!bin_out && echoin) { @@ -115,53 +120,53 @@ static void process(FILE *fid) { } } - if (data.u != HUGE_VAL) { - if (prescale) { data.u *= fscale; data.v *= fscale; } + if (data.uv.u != HUGE_VAL) { + if (prescale) { data.uv.u *= fscale; data.uv.v *= fscale; } if (dofactors && !inverse) - facs_bad = pj_factors(data, Proj, 0., &facs); - data = (*proj)(data, Proj); + facs_bad = pj_factors(data.lp, Proj, 0., &facs); + data.xy = (*proj.fwd)(data.lp, Proj); if (dofactors && inverse) - facs_bad = pj_factors(data, Proj, 0., &facs); + facs_bad = pj_factors(data.lp, Proj, 0., &facs); - if (postscale && data.u != HUGE_VAL) - { data.u *= fscale; data.v *= fscale; } + if (postscale && data.uv.u != HUGE_VAL) + { data.uv.u *= fscale; data.uv.v *= fscale; } } if (bin_out) { /* binary output */ (void)fwrite(&data, sizeof(projUV), 1, stdout); continue; - } else if (data.u == HUGE_VAL) /* error output */ + } else if (data.uv.u == HUGE_VAL) /* error output */ (void)fputs(oterr, stdout); else if (inverse && !oform) { /*ascii DMS output */ if (reverseout) { - (void)fputs(rtodms(pline, data.v, 'N', 'S'), stdout); + (void)fputs(rtodms(pline, data.uv.v, 'N', 'S'), stdout); putchar('\t'); - (void)fputs(rtodms(pline, data.u, 'E', 'W'), stdout); + (void)fputs(rtodms(pline, data.uv.u, 'E', 'W'), stdout); } else { - (void)fputs(rtodms(pline, data.u, 'E', 'W'), stdout); + (void)fputs(rtodms(pline, data.uv.u, 'E', 'W'), stdout); putchar('\t'); - (void)fputs(rtodms(pline, data.v, 'N', 'S'), stdout); + (void)fputs(rtodms(pline, data.uv.v, 'N', 'S'), stdout); } } else { /* x-y or decimal degree ascii output, scale if warranted by output units */ if (inverse) { if (Proj->left == PJ_IO_UNITS_RADIANS) { - data.v *= RAD_TO_DEG; - data.u *= RAD_TO_DEG; + data.uv.v *= RAD_TO_DEG; + data.uv.u *= RAD_TO_DEG; } } else { if (Proj->right == PJ_IO_UNITS_RADIANS) { - data.v *= RAD_TO_DEG; - data.u *= RAD_TO_DEG; + data.uv.v *= RAD_TO_DEG; + data.uv.u *= RAD_TO_DEG; } } if (reverseout) { - (void)printf(oform,data.v); putchar('\t'); - (void)printf(oform,data.u); + (void)printf(oform, data.uv.v); putchar('\t'); + (void)printf(oform, data.uv.u); } else { - (void)printf(oform,data.u); putchar('\t'); - (void)printf(oform,data.v); + (void)printf(oform, data.uv.u); putchar('\t'); + (void)printf(oform, data.uv.v); } } @@ -181,7 +186,8 @@ static void process(FILE *fid) { /* file processing function --- verbosely */ static void vprocess(FILE *fid) { char line[MAX_LINE+3], *s, pline[40]; - projUV dat_ll, dat_xy, temp; + LP dat_ll; + projXY dat_xy; int linvers; @@ -224,41 +230,39 @@ static void vprocess(FILE *fid) { emess(-1,"inverse for this projection not avail.\n"); continue; } - dat_xy.u = strtod(s, &s); - dat_xy.v = strtod(s, &s); - if (dat_xy.u == HUGE_VAL || dat_xy.v == HUGE_VAL) { + dat_xy.x = strtod(s, &s); + dat_xy.y = strtod(s, &s); + if (dat_xy.x == HUGE_VAL || dat_xy.y == HUGE_VAL) { emess(-1,"lon-lat input conversion failure\n"); continue; } - if (prescale) { dat_xy.u *= fscale; dat_xy.v *= fscale; } + if (prescale) { dat_xy.x *= fscale; dat_xy.y *= fscale; } if (reversein) { - temp.u = dat_xy.u; - temp.v = dat_xy.v; - dat_xy.u = temp.v; - dat_xy.v = temp.u; + projXY temp = dat_xy; + dat_xy.x = temp.y; + dat_xy.y = temp.x; } dat_ll = pj_inv(dat_xy, Proj); } else { - dat_ll.u = dmstor(s, &s); - dat_ll.v = dmstor(s, &s); - if (dat_ll.u == HUGE_VAL || dat_ll.v == HUGE_VAL) { + dat_ll.lam = dmstor(s, &s); + dat_ll.phi = dmstor(s, &s); + if (dat_ll.lam == HUGE_VAL || dat_ll.phi == HUGE_VAL) { emess(-1,"lon-lat input conversion failure\n"); continue; } if (reversein) { - temp.u = dat_ll.u; - temp.v = dat_ll.v; - dat_ll.u = temp.v; - dat_ll.v = temp.u; + LP temp = dat_ll; + dat_ll.lam = temp.phi; + dat_ll.phi = temp.lam; } dat_xy = pj_fwd(dat_ll, Proj); - if (postscale) { dat_xy.u *= fscale; dat_xy.v *= fscale; } + if (postscale) { dat_xy.x *= fscale; dat_xy.y *= fscale; } } /* apply rad->deg scaling in case the output from a pipeline has degrees as units */ if (!inverse && Proj->right == PJ_IO_UNITS_RADIANS) { - dat_xy.u *= RAD_TO_DEG; - dat_xy.v *= RAD_TO_DEG; + dat_xy.x *= RAD_TO_DEG; + dat_xy.y *= RAD_TO_DEG; } /* For some reason pj_errno does not work as expected in some */ @@ -278,15 +282,15 @@ static void vprocess(FILE *fid) { (void)fputs(s, stdout); (void)fputs("Longitude: ", stdout); - (void)fputs(rtodms(pline, dat_ll.u, 'E', 'W'), stdout); - (void)printf(" [ %.11g ]\n", dat_ll.u * RAD_TO_DEG); + (void)fputs(rtodms(pline, dat_ll.lam, 'E', 'W'), stdout); + (void)printf(" [ %.11g ]\n", dat_ll.lam * RAD_TO_DEG); (void)fputs("Latitude: ", stdout); - (void)fputs(rtodms(pline, dat_ll.v, 'N', 'S'), stdout); - (void)printf(" [ %.11g ]\n", dat_ll.v * RAD_TO_DEG); + (void)fputs(rtodms(pline, dat_ll.phi, 'N', 'S'), stdout); + (void)printf(" [ %.11g ]\n", dat_ll.phi * RAD_TO_DEG); (void)fputs("Easting (x): ", stdout); - (void)printf(oform, dat_xy.u); putchar('\n'); + (void)printf(oform, dat_xy.x); putchar('\n'); (void)fputs("Northing (y): ", stdout); - (void)printf(oform, dat_xy.v); putchar('\n'); + (void)printf(oform, dat_xy.y); putchar('\n'); (void)printf("Meridian scale (h) : %.8f ( %.4g %% error )\n", facs.h, (facs.h-1.)*100.); (void)printf("Parallel scale (k) : %.8f ( %.4g %% error )\n", facs.k, (facs.k-1.)*100.); (void)printf("Areal scale (s): %.8f ( %.4g %% error )\n", facs.s, (facs.s-1.)*100.); @@ -489,9 +493,9 @@ int main(int argc, char **argv) { if (inverse) { if (!Proj->inv) emess(3,"inverse projection not available"); - proj = pj_inv; + proj.inv = pj_inv; } else - proj = pj_fwd; + proj.fwd = pj_fwd; if (cheby_str) { gen_cheb(inverse, int_proj, cheby_str, Proj, iargc, iargv); exit(0); -- cgit v1.2.3 From 8ed970e6856ed3d61ab8cc827899a8cced0591d0 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 14 Dec 2017 09:19:37 +0100 Subject: Updates to deformation operation. The initial approach taken in the deformation operation was not geodetically sound. The deformation model grids were required to be indexed in lat/long space with the velocities in the grids being in cartesian space. This is quite confusing and it is not a normal way of making deformation models. The usual approach is to keep everything in the east, north, up, or ENU, space. We adopt that tradition in this commit. The velocities are still applied in cartesian space which requires that the grid-velocities are converted from ENU space to cartesian space. As a consequence of this change the operation is changed so that it only works in full 3D mode. That is, both horizontal and vertical grids need to be applied. The inverse operation is changed slightly to accommodate the now fully 3D transformation. In it's present form it is a modification to the original algorithm that also includes the vertical component in the iteration. This is necessary to get a proper mapping from ENU to cartesian space in the loop. The vertical component is overwritten with the initial z-correction at the end of the loop. This approach is not completely accurate and will introduce errors, especially when doing many roundtrip calculations, but it seems to be good enough for a few roundtrips. The PJ_ENU data type is re-introduced to better communicate the what state the grid corrections are in throughout the code. --- src/PJ_deformation.c | 172 +++++++++++++++++++++++++++++---------------------- src/proj.h | 2 + 2 files changed, 101 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/PJ_deformation.c b/src/PJ_deformation.c index 09692ccb..d227e49f 100644 --- a/src/PJ_deformation.c +++ b/src/PJ_deformation.c @@ -13,18 +13,21 @@ Perform datum shifts by means of a deformation/velocity model. Z_out = Z_in + (T_ct - T_obs)*DZ -Corrections are done in cartesian space. Coordinates of the -gridded model are in lat/long space and the corrections in the model are -in cartesian space. Hence the input coordinates needs to be converted -to lat/long space when searching for corrections in the grid. The -corrections are then applied to the input coordinates in cartesian -space. +The deformation operation takes cartesian coordinates as input and +returns cartesian coordinates as well. + +Corrections in the gridded model are in east, north, up (ENU) space. +Hence the input coordinates needs to be converted to ENU-space when +searching for corrections in the grid. The corrections are then converted +to cartesian XYZ-space and applied to the input coordinates (also in +cartesian space). A full deformation model is described by two grids, one for the horizontal components and one for the vertical component. The horizontal grid is stored in CTable/CTable2 and the vertical grid is stored in the GTX -format. Both grids are expected to contain grid-values in units of -m/year. +format. The NTv2 format should not be used for this purpose since grid- +values are scaled upon reading. Both grids are expected to contain +grid-values in units of mm/year in ENU-space. ************************************************************************ * Copyright (c) 2017, Kristian Evers @@ -62,50 +65,91 @@ PROJ_HEAD(deformation, "Kinematic grid shift"); struct pj_opaque { double t_obs; double t_epoch; - int has_xy_grids, has_z_grids; PJ *cart; }; -static XYZ get_xygrid_shift(PJ* P, XYZ cartesian) { - PJ_COORD geodetic, shift; +/********************************************************************************/ +static XYZ get_grid_shift(PJ* P, XYZ cartesian) { +/******************************************************************************** + Read correction values from grid. The cartesian input coordinates are + converted to geodetic coordinates in order look up the correction values + in the grid. Once the grid corrections are read we need to convert them + from ENU-space to cartesian XYZ-space. ENU -> XYZ formula described in: + + Nørbech, T., et al, 2003(?), "Transformation from a Common Nordic Reference + Frame to ETRS89 in Denmark, Finland, Norway, and Sweden – status report" + +********************************************************************************/ + PJ_COORD geodetic, shift, temp; + double sp, cp, sl, cl; + /* cartesian to geodetic */ geodetic.lpz = pj_inv3d(cartesian, P->opaque->cart); - shift.lp = proj_hgrid_value(P, geodetic.lp); - return shift.xyz; -} + /* look up correction values in grids */ + shift.lp = proj_hgrid_value(P, geodetic.lp); + shift.enu.u = proj_vgrid_value(P, geodetic.lp); -static double get_zgrid_shift(PJ* P, XYZ cartesian) { - PJ_COORD geodetic; + /* grid values are stored as mm/yr, we need m/yr */ + shift.xyz.x /= 1000; + shift.xyz.y /= 1000; + shift.xyz.z /= 1000; - geodetic.lpz = pj_inv3d(cartesian, P->opaque->cart); + /* pre-calc cosines and sines */ + sp = sin(geodetic.lp.phi); + cp = cos(geodetic.lp.phi); + sl = sin(geodetic.lp.lam); + cl = cos(geodetic.lp.lam); - return proj_vgrid_value(P, geodetic.lp); + /* ENU -> XYZ */ + temp.xyz.x = -sp*cl*shift.enu.n - sl*shift.enu.e + cp*cl*shift.enu.u; + temp.xyz.y = -sp*sl*shift.enu.n + cl*shift.enu.e + cp*sl*shift.enu.u; + temp.xyz.z = cp*shift.enu.n + sp*shift.enu.u; + + shift.xyz = temp.xyz; + + return shift.xyz; } -static XYZ reverse_hshift(PJ *P, XYZ input, double dt) { +/********************************************************************************/ +static XYZ reverse_shift(PJ *P, XYZ input, double dt) { +/******************************************************************************** + Iteratively determine the reverse grid shift correction values. +*********************************************************************************/ XYZ out, delta, dif; + double z0; int i = MAX_ITERATIONS; - delta = get_xygrid_shift(P, input); + delta = get_grid_shift(P, input); - out.x = input.x + dt*delta.x; + /* Store the origial z shift for later application */ + z0 = delta.z; + + /* When iterating to find the best horizontal coordinate we also carry */ + /* along the z-component, since we need it for the cartesian -> geodetic */ + /* conversion. The z-component adjustment is overwritten with z0 after */ + /* the loop has finished. */ + out.x = input.x - dt*delta.x; out.y = input.y - dt*delta.y; - out.z = input.z; + out.z = input.z + dt*delta.z; do { - delta = get_xygrid_shift(P, out); + delta = get_grid_shift(P, out); if (delta.x == HUGE_VAL) break; - dif.x = out.x - dt*delta.x - input.x; + dif.x = out.x + dt*delta.x - input.x; dif.y = out.y + dt*delta.y - input.y; + dif.z = out.z - dt*delta.z - input.z; out.x += dif.x; out.y += dif.y; + out.z += dif.z; } while ( --i && hypot(dif.x, dif.y) > TOL ); + out.z = input.z - dt*z0; + return out; } @@ -118,22 +162,18 @@ static XYZ forward_3d(LPZ lpz, PJ *P) { out = in; if (Q->t_obs != HUGE_VAL) { - dt = Q->t_obs - Q->t_epoch; + dt = Q->t_epoch - Q->t_obs; } else { out = proj_coord_error(); /* in the 3D case +t_obs must be specified */ proj_log_debug(P, "deformation: +t_obs must be specified"); return out.xyz; } - if (Q->has_xy_grids) { - shift = get_xygrid_shift(P, in.xyz); + shift = get_grid_shift(P, in.xyz); - out.xyz.x += dt * shift.x; - out.xyz.y += dt * shift.y; - } - - if (Q->has_z_grids) - out.xyz.z += dt * get_zgrid_shift(P, in.xyz); + out.xyz.x += dt * shift.x; + out.xyz.y += dt * shift.y; + out.xyz.z += dt * shift.z; return out.xyz; } @@ -146,19 +186,17 @@ static PJ_COORD forward_4d(PJ_COORD in, PJ *P) { PJ_COORD out = in; if (Q->t_obs != HUGE_VAL) { - dt = Q->t_obs - Q->t_epoch; + dt = Q->t_epoch - Q->t_obs; } else { - dt = in.xyzt.t - Q->t_epoch; + dt = Q->t_epoch - in.xyzt.t; } - if (Q->has_xy_grids) { - shift = get_xygrid_shift(P, in.xyz); - out.xyz.x += dt * shift.x; - out.xyz.y += dt * shift.y; - } + shift = get_grid_shift(P, in.xyz); + + out.xyzt.x += dt*shift.x; + out.xyzt.y += dt*shift.y; + out.xyzt.z += dt*shift.z; - if (Q->has_z_grids) - out.xyz.z += dt * get_zgrid_shift(P, in.xyz); return out; } @@ -178,12 +216,7 @@ static LPZ reverse_3d(XYZ in, PJ *P) { return out.lpz; } - if (Q->has_xy_grids) { - out.xyz = reverse_hshift(P, in, dt); - } - - if (Q->has_z_grids) - out.xyz.z = in.z + dt * get_zgrid_shift(P, in); + out.xyz = reverse_shift(P, in, dt); return out.lpz; } @@ -193,18 +226,14 @@ static PJ_COORD reverse_4d(PJ_COORD in, PJ *P) { PJ_COORD out = in; double dt; + if (Q->t_obs != HUGE_VAL) { dt = Q->t_epoch - Q->t_obs; } else { dt = Q->t_epoch - in.xyzt.t; } - if (Q->has_xy_grids) - out.xyz = reverse_hshift(P, in.xyz, dt); - - if (Q->has_z_grids) - out.xyz.z = in.xyz.z + dt * get_zgrid_shift(P, in.xyz); - + out.xyz = reverse_shift(P, in.xyz, dt); return out; } @@ -223,9 +252,11 @@ static void *destructor(PJ *P, int errlev) { PJ *TRANSFORMATION(deformation,1) { + int has_xy_grids = 0; + int has_z_grids = 0; struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); if (0==Q) - return pj_default_destructor(P, ENOMEM); + return destructor(P, ENOMEM); P->opaque = (void *) Q; Q->cart = proj_create(P->ctx, "+proj=cart"); @@ -235,30 +266,25 @@ PJ *TRANSFORMATION(deformation,1) { /* inherit ellipsoid definition from P to Q->cart */ pj_inherit_ellipsoid_def (P, Q->cart); - Q->has_xy_grids = pj_param(P->ctx, P->params, "txy_grids").i; - Q->has_z_grids = pj_param(P->ctx, P->params, "tz_grids").i; + has_xy_grids = pj_param(P->ctx, P->params, "txy_grids").i; + has_z_grids = pj_param(P->ctx, P->params, "tz_grids").i; - /* Build gridlists. P->gridlist and P->vgridlist_geoid can be empty if */ - /* +xy_grids or +z_grids only ask for optional grids */ - if (!Q->has_xy_grids && !Q->has_z_grids) { - proj_log_error(P, "deformation: At least one of either +xy_grids or +z_grids should be specified."); + /* Build gridlists. Both horizontal and vertical grids are mandatory. */ + if (!has_xy_grids || !has_z_grids) { + proj_log_error(P, "deformation: Both +xy_grids and +z_grids should be specified."); return destructor(P, PJD_ERR_NO_ARGS ); } - if (Q->has_xy_grids) { - Q->has_xy_grids = proj_hgrid_init(P, "xy_grids"); - if (proj_errno(P)) { - proj_log_error(P, "deformation: could not find requested grid(s)."); - return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } + proj_hgrid_init(P, "xy_grids"); + if (proj_errno(P)) { + proj_log_error(P, "deformation: could not find requested xy_grid(s)."); + return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); } - if (Q->has_z_grids) { - Q->has_z_grids = proj_vgrid_init(P, "z_grids"); - if (proj_errno(P)) { - proj_log_error(P, "deformation: could not find requested grid(s)."); - return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } + proj_vgrid_init(P, "z_grids"); + if (proj_errno(P)) { + proj_log_error(P, "deformation: could not find requested z_grid(s)."); + return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); } Q->t_obs = HUGE_VAL; diff --git a/src/proj.h b/src/proj.h index aa87ae7d..67c28b8d 100644 --- a/src/proj.h +++ b/src/proj.h @@ -206,6 +206,7 @@ typedef struct { double x, y, z, t; } PJ_XYZT; typedef struct { double u, v, w, t; } PJ_UVWT; typedef struct { double lam, phi, z, t; } PJ_LPZT; typedef struct { double o, p, k; } PJ_OPK; /* Rotations: omega, phi, kappa */ +typedef struct { double e, n, u; } PJ_ENU; /* East, North, Up */ /* Classic proj.4 pair/triplet types */ typedef struct { double u, v; } UV; @@ -224,6 +225,7 @@ union PJ_COORD { PJ_UVWT uvwt; PJ_LPZT lpzt; PJ_OPK opk; + PJ_ENU enu; XYZ xyz; UVW uvw; LPZ lpz; -- cgit v1.2.3 From 0c2bf456e4e039ffbb4fe5ede8ed4ee069aff0e4 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 14 Dec 2017 17:11:04 +0100 Subject: Add time unit yyyymmdd to unitconvert operation (#707) Add time unit yyyymmdd to unitconvert operation --- src/PJ_unitconvert.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/gie.c | 6 +++- 2 files changed, 83 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c index f951ebb6..36461150 100644 --- a/src/PJ_unitconvert.c +++ b/src/PJ_unitconvert.c @@ -94,6 +94,39 @@ static int days_in_year(int year) { return is_leap_year(year) ? 366 : 365; } +/***********************************************************************/ +static unsigned int days_in_month(unsigned int year, unsigned int month) { +/***********************************************************************/ + const unsigned int month_table[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + unsigned int days; + + if (month > 12) month = 12; + if (month == 0) month = 1; + + days = month_table[month-1]; + if (is_leap_year(year) && month == 2) days++; + + return days; +} + + +/***********************************************************************/ +static int daynumber_in_year(unsigned int year, unsigned int month, unsigned int day) { +/***********************************************************************/ + unsigned int daynumber=0, i; + + if (month > 12) month = 12; + if (month == 0) month = 1; + if (day > days_in_month(year, month)) day = days_in_month(year, month); + + for (i = 1; i < month; i++) + daynumber += days_in_month(year, i); + + daynumber += day; + + return daynumber; + +} /***********************************************************************/ static double mjd_to_mjd(double mjd) { @@ -181,10 +214,53 @@ static double mjd_to_gps_week(double mjd) { return (mjd - 44244.0) / 7.0; } + +/***********************************************************************/ +static double yyyymmdd_to_mjd(double yyyymmdd) { +/************************************************************************ + Date given in YYYY-MM-DD format. +************************************************************************/ + + int year = (int)floor( yyyymmdd / 10000 ); + int month = (int)floor((yyyymmdd - year*10000) / 100); + int day = (int)floor( yyyymmdd - year*10000 - month*100); + double mjd = daynumber_in_year(year, month, day); + + for (year -= 1; year > 1858; year--) + mjd += days_in_year(year); + + return mjd + 13 + 31; +} + + +/***********************************************************************/ +static double mjd_to_yyyymmdd(double mjd) { +/************************************************************************ + Date given in YYYY-MM-DD format. +************************************************************************/ + double mjd_iter = 14 + 31; + int year = 1859, month=0, day=0; + + for (; mjd >= mjd_iter; year++) { + mjd_iter += days_in_year(year); + } + year--; + mjd_iter -= days_in_year(year); + + for (month=1; mjd_iter + days_in_month(year, month) <= mjd; month++) + mjd_iter += days_in_month(year, month); + + day = (int)(mjd - mjd_iter + 1); + + return year*10000 + month*100 + day; +} + + struct TIME_UNITS time_units[] = { {"mjd", mjd_to_mjd, mjd_to_mjd, "Modified julian date"}, {"decimalyear", decimalyear_to_mjd, mjd_to_decimalyear, "Decimal year"}, {"gps_week", gps_week_to_mjd, mjd_to_gps_week, "GPS Week"}, + {"yyyymmdd", yyyymmdd_to_mjd, mjd_to_yyyymmdd, "YYYYMMDD date"}, {NULL, NULL, NULL, NULL} }; @@ -323,8 +399,8 @@ PJ *CONVERSION(unitconvert,0) { P->fwd = forward_2d; P->inv = reverse_2d; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_METERS; + P->right = PJ_IO_UNITS_METERS; /* if no time input/output unit is specified we can skip them */ Q->t_in_id = -1; diff --git a/src/gie.c b/src/gie.c index fa7921dd..c768c765 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1698,12 +1698,16 @@ static int pj_unitconvert_selftest (void) { char args6[] = "+proj=unitconvert +xy_in=m +xy_out=m +z_in=m +z_out=m"; PJ_COORD in6 = {{12.3, 45.6, 7.89, 0}}; + char args7[] = "+proj=unitconvert +t_in=yyyymmdd +t_out=yyyymmdd"; + double in7 = 20170131; + ret = test_time(args1, 1e-6, in1, in1); if (ret) return ret + 10; ret = test_time(args2, 1e-6, in2, in2); if (ret) return ret + 20; ret = test_time(args3, 1e-6, in3, in3); if (ret) return ret + 30; ret = test_time(args4, 1e-6, in4, exp4); if (ret) return ret + 40; ret = test_xyz (args5, 1e-10, in5, exp5); if (ret) return ret + 50; - ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 50; + ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 60; + ret = test_time(args7, 1e-6, in7, in7); if (ret) return ret + 70; return 0; -- cgit v1.2.3 From 533207012bfd2c5de652b9df8b2104cad82b6988 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Sun, 17 Dec 2017 17:54:28 +0100 Subject: Fix integer overflow in unitconvert. For sufficiently large values of modified julian date the mjd_to_yyyymmdd function would integer overflow in the calculation of the return value. This is fixed by implicit type conversion. Credit to OSS-Fuzz. https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=4658 and https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=4667 --- src/PJ_unitconvert.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c index 36461150..fc90821b 100644 --- a/src/PJ_unitconvert.c +++ b/src/PJ_unitconvert.c @@ -252,7 +252,7 @@ static double mjd_to_yyyymmdd(double mjd) { day = (int)(mjd - mjd_iter + 1); - return year*10000 + month*100 + day; + return year*10000.0 + month*100.0 + day; } -- cgit v1.2.3 From 74ae4b09bbc80edb44a123a8272014d15b7d4b8d Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sun, 17 Dec 2017 18:04:13 +0100 Subject: Free format everywhere (#693) * Free format now in cmd lines, in gie, and in init files * Corrected handling of defaults * Add demo of integrated definition and validation * Repair stack-smashing memmove in get_init * repair paralist corruption, clean up debug output * Install test files for nmake builds * Add many improvements following suggestions by @schwehr * Be consistent in requiring lower case everywhere in gie.c Also, this Fixes #703 and Fixes #697 --- src/PJ_pipeline.c | 9 +- src/cct.c | 15 +- src/gie.c | 676 +++++++++++++++++++++++++++++++++------------------- src/makefile.vc | 3 + src/optargpm.h | 33 ++- src/pj_factors.c | 15 +- src/pj_init.c | 636 +++++++++++++++++++++++++----------------------- src/pj_internal.c | 288 +++++++++++++++++++--- src/pj_malloc.c | 3 +- src/pj_param.c | 217 +++++++++++------ src/proj.def | 2 + src/proj_4D_api.c | 102 +++----- src/proj_internal.h | 7 + src/projects.h | 6 +- 14 files changed, 1274 insertions(+), 738 deletions(-) (limited to 'src') diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index c8ce8582..a0ef7c1e 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -51,12 +51,9 @@ pj_init code, implementing support for multilevel, embedded pipelines. Syntactically, the pipeline system introduces the "+step" keyword (which - indicates the start of each transformation step), the "+omit_fwd" and - "+omit_inv" keywords (which indicate that a given transformation step - should be omitted when the pipeline is executed in forward, resp. inverse - direction), and reintroduces the +inv keyword (indicating that a given - transformation step should run in reverse, i.e. forward, when the pipeline - is executed in inverse direction, and vice versa). + indicates the start of each transformation step), and reintroduces the +inv + keyword (indicating that a given transformation step should run in reverse, i.e. + forward, when the pipeline is executed in inverse direction, and vice versa). Hence, the first transformation example above, can be implemented as: diff --git a/src/cct.c b/src/cct.c index a1c275b5..eae815e9 100644 --- a/src/cct.c +++ b/src/cct.c @@ -71,20 +71,23 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26 ***********************************************************************/ -#include "optargpm.h" -#include "proj_internal.h" -#include -#include "projects.h" +#include +#include #include #include -#include #include -#include + +#include +#include "proj_internal.h" +#include "projects.h" +#include "optargpm.h" +/* Prototypes for functions in proj_strtod.c */ double proj_strtod(const char *str, char **endptr); double proj_atof(const char *str); +/* Prototypes from functions in this file */ char *column (char *buf, int n); PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time); diff --git a/src/gie.c b/src/gie.c index c768c765..e23ca92b 100644 --- a/src/gie.c +++ b/src/gie.c @@ -15,9 +15,9 @@ C compiler already employed for compiling the library. The basic functionality of the gie command language is implemented through just 3 command verbs: -OPERATION, which defines the PROJ.4 operation to test, -ACCEPT, which defines the input coordinate to read, and -EXPECT, which defines the result to expect. +operation, which defines the PROJ.4 operation to test, +accept, which defines the input coordinate to read, and +expect, which defines the result to expect. E.g: @@ -32,7 +32,7 @@ long strings of numbers typically required in projected coordinates. By default, gie considers the EXPECTation met, if the result comes to within 0.5 mm of the expected. This default can be changed using the -TOLERANCE command verb (and yes, I know, linguistically speaking, both +'tolerance' command verb (and yes, I know, linguistically speaking, both "operation" and "tolerance" are nouns, not verbs). See the first few hundred lines of the "builtins.gie" test file for more details of the command verbs available (verbs of both the VERBal and NOUNistic @@ -64,15 +64,16 @@ hence making a map projection function call, pj_fwd(PJ, point), as easy as a traditional function call like hypot(x,y). While today, we may have more formally well defined metadata systems -(most prominent the OGC WKT representation), nothing comes close being +(most prominent the OGC WKT2 representation), nothing comes close being as easily readable ("human compatible") as Gerald's key-value system. This system in particular, and the PROJ.4 system in general, was Gerald's great gift to anyone using and/or communicating about geodata. -It is only reasonable to name a program keeping an eye on the integrity -of the PROJ.4 system in honour of Gerald. So in honour, and hopefully -also in the spirit, of Gerald Ian Evenden (1935--2016), this is the -Geospatial Integrity Investigation Environment. +It is only reasonable to name a program, keeping an eye on the integrity +of the PROJ.4 system, in honour of Gerald. + +So in honour, and hopefully also in the spirit, of Gerald Ian Evenden +(1935--2016), this is the Geospatial Integrity Investigation Environment. ************************************************************************ @@ -103,22 +104,54 @@ Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08 ***********************************************************************/ -#include "optargpm.h" +#include +#include +#include +#include +#include +#include +#include #include #include "proj_internal.h" #include "projects.h" -#include -#include -#include - -#include -#include +#include "optargpm.h" -#include -#include +/* Package for flexible format I/O - ffio */ +typedef struct ffio { + FILE *f; + const char **tags; + const char *tag; + char *args; + char *next_args; + size_t n_tags; + size_t args_size; + size_t next_args_size; + size_t argc; + size_t lineno, next_lineno; + size_t level; +} ffio; + +FILE *test = 0; + +static int get_inp (ffio *F); +static int skip_to_next_tag (ffio *F); +static int step_into_gie_block (ffio *F); +static int locate_tag (ffio *F, const char *tag); +static int nextline (ffio *F); +static int at_end_delimiter (ffio *F); +static const char *at_tag (ffio *F); +static int at_decorative_element (ffio *F); +static ffio *ffio_destroy (ffio *F); +static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size); + +static const char *gie_tags[] = { + "", "operation", "accept", "expect", "roundtrip", "banner", "verbose", + "direction", "tolerance", "builtins", "echo", "" +}; +static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0]; /* from proj_strtod.c */ @@ -127,16 +160,14 @@ double proj_atof(const char *str); int main(int argc, char **argv); -static int process_file (const char *fname); -static int errmsg (int errlev, const char *msg, ...); -static int get_inp (FILE *f, char *inp, int size); -static int get_cmnd (const char *inp, char *cmnd, int len); -static const char *get_args (const char *inp); static int dispatch (const char *cmnd, const char *args); +static int errmsg (int errlev, const char *msg, ...); +static int errno_from_err_const (const char *err_const); +static int list_err_codes (void); +static int process_file (const char *fname); + static const char *column (const char *buf, int n); -static int errno_from_err_const (const char *err_const); static const char *err_const_from_errno (int err); -static void list_err_codes (void); @@ -148,7 +179,6 @@ typedef struct { PJ_COORD a, b, c, e; PJ_DIRECTION dir; int verbosity; - int nargs; int op_id; int op_ok, op_ko; int total_ok, total_ko; @@ -159,20 +189,15 @@ typedef struct { FILE *fout; } gie_ctx; -gie_ctx T = {{""}, 0, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}}, PJ_FWD, 1, 0, 0,0,0,0,0,0,0,0, 0.0005, 0, 0}; +ffio *F = 0; -OPTARGS *o; +static gie_ctx T; +static const char delim[] = {"-------------------------------------------------------------------------------\n"}; -size_t tol_lineno = 0; -size_t lineno = 0; -size_t level = 0; -char delim[] = {"-------------------------------------------------------------------------------\n"}; -char DELIM[] = {"===============================================================================\n"}; - -#define CMDLEN 25000 +#define CMDLEN 250000 int nfiles = 0; @@ -212,7 +237,14 @@ int main (int argc, char **argv) { int i; const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", 0}; const char *longkeys[] = {"o=output", 0}; + OPTARGS *o; + + memset (&T, 0, sizeof (T)); + T.dir = PJ_FWD; + T.verbosity = 1; + T.tolerance = 5e-4; + test = fopen ("test.c", "wt"); o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys); if (0==o) return 0; @@ -223,7 +255,7 @@ int main (int argc, char **argv) { } if (opt_given (o, "l")) - list_err_codes (); + return list_err_codes (); T.verbosity = opt_given (o, "q"); @@ -235,6 +267,7 @@ int main (int argc, char **argv) { T.fout = stdout; if (opt_given (o, "o")) T.fout = fopen (opt_arg (o, "output"), "rt"); + if (0==T.fout) { fprintf (stderr, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output")); free (o); @@ -245,10 +278,18 @@ int main (int argc, char **argv) { if (T.verbosity==-1) return -1; fprintf (T.fout, "Nothing to do\n"); + free (o); return 0; } - for (i = 0; i < o->fargc; i++) + F = ffio_create (gie_tags, n_gie_tags, 1000); + if (0==F) { + fprintf (stderr, "%s: No memory\n", o->progname); + free (o); + return 1; + } + + for (i = 0; i < o->fargc; i++) process_file (o->fargv[i]); if (T.verbosity > 0) { @@ -264,6 +305,7 @@ int main (int argc, char **argv) { fclose (T.fout); free (o); + ffio_destroy (F); return T.grand_ko; } @@ -282,11 +324,8 @@ static int another_success (void) { static int process_file (const char *fname) { FILE *f; - char inp[CMDLEN]; - char cmnd[1000]; - const char *args; - lineno = level = 0; + F->lineno = F->next_lineno = F->level = 0; T.op_ok = T.total_ok = 0; T.op_ko = T.total_ko = 0; @@ -298,37 +337,29 @@ static int process_file (const char *fname) { } errmsg (2, "Cannot open spec'd input file '%s' - bye!\n", fname); } + F->f = f; + if (T.verbosity > 0) fprintf (T.fout, "%sReading file '%s'\n", delim, fname); T.curr_file = fname; - while (get_inp(f, inp, CMDLEN)) { - int len; - - if (feof(f)) - break; - len = get_cmnd (inp, cmnd, 1000); - if (len>=999) { - errmsg (2, "Command verb too long: '%s' - bye!\n", cmnd); - proj_destroy (T.P); - T.P = 0; - return 0; - } - args = get_args (inp); - if (SKIP==dispatch (cmnd, args)) + while (get_inp(F)) { + if (SKIP==dispatch (F->tag, F->args)) return proj_destroy (T.P), T.P = 0, 0; } + fclose (f); + F->lineno = F->next_lineno = 0; T.grand_ok += T.total_ok; T.grand_ko += T.total_ko; if (T.verbosity > 0) fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests %s\n", delim, T.total_ok, T.total_ko, T.total_ko? "FAILED!": "failed."); - if (level==0) - return errmsg (-3, "File '%s':Missing 'BEGIN' cmnd - bye!\n", fname); - if (level && level%2) - return errmsg (-4, "File '%s':Missing 'END' cmnd - bye!\n", fname); + if (F->level==0) + return errmsg (-3, "File '%s':Missing '' cmnd - bye!\n", fname); + if (F->level && F->level%2) + return errmsg (-4, "File '%s':Missing '' cmnd - bye!\n", fname); return 0; } @@ -336,7 +367,7 @@ static int process_file (const char *fname) { /*****************************************************************************/ const char *column (const char *buf, int n) { /***************************************************************************** - Return a pointer to the n'th column of buf. Coulmn numbers start at 0. +Return a pointer to the n'th column of buf. Column numbers start at 0. ******************************************************************************/ int i; if (n <= 0) @@ -356,8 +387,8 @@ const char *column (const char *buf, int n) { /*****************************************************************************/ static double strtod_scaled (const char *args, double default_scale) { /***************************************************************************** - Interpret as a numeric followed by a linear decadal prefix. - Return the properly scaled numeric +Interpret as a numeric followed by a linear decadal prefix. +Return the properly scaled numeric ******************************************************************************/ double s; const char *endp = args; @@ -424,6 +455,7 @@ static int direction (const char *args) { default: return 1; } + return 0; } @@ -439,46 +471,38 @@ static void finish_previous_operation (const char *args) { /*****************************************************************************/ static int operation (char *args) { /***************************************************************************** - Define the operation to apply to the input data (in ISO 19100 lingo, - an operation is the general term describing something that can be - either a conversion or a transformation) +Define the operation to apply to the input data (in ISO 19100 lingo, +an operation is the general term describing something that can be +either a conversion or a transformation) ******************************************************************************/ - int i, j, n; T.op_id++; - T.operation_lineno = lineno; - - /* compactify the args, so we can fit more info on a line in verbose mode */ - n = (int) strlen (args); - for (i = j = 0; i < n; ) { - /* skip prefix whitespace */ - while (isspace (args[i])) - i++; - /* move a whitespace delimited text string to the left, skipping over superfluous whitespace */ - while ((0!=args[i]) && (!isspace (args[i]))) - args[j++] = args[i++]; - if (args[j+1]!=0) - args[j++] = ' '; - i++; - } - args[j++] = 0; - strcpy (&(T.operation[0]), args); + T.operation_lineno = F->lineno; + + strcpy (&(T.operation[0]), F->args); if (T.verbosity > 1) { - finish_previous_operation (args); + finish_previous_operation (F->args); banner (args); } + T.op_ok = 0; T.op_ko = 0; direction ("forward"); tolerance ("0.5 mm"); + proj_errno_reset (T.P); if (T.P) proj_destroy (T.P); - T.P = proj_create (0, args); + proj_errno_reset (0); + + T.P = proj_create (0, F->args); + + /* Checking that proj_create succeeds is first done at "expect" time, */ + /* since we want to support "expect"ing specific error codes */ return 0; } @@ -493,9 +517,9 @@ static int pj_horner_selftest (void); /*****************************************************************************/ static int builtins (const char *args) { /***************************************************************************** - There are still a few tests that cannot be described using gie - primitives. Instead, they are implemented as builtins, and invoked - using the "builtins" command verb. +There are still a few tests that cannot be described using gie +primitives. Instead, they are implemented as builtins, and invoked +using the "builtins" command verb. ******************************************************************************/ int i; if (T.verbosity > 1) { @@ -504,10 +528,9 @@ static int builtins (const char *args) { } T.op_ok = 0; T.op_ko = 0; - i = pj_unitconvert_selftest (); if (i!=0) { - printf ("pj_unitconvert_selftest fails with %d\n", i); + fprintf (T.fout, "pj_unitconvert_selftest fails with %d\n", i); another_failure(); } else @@ -516,7 +539,7 @@ static int builtins (const char *args) { i = pj_cart_selftest (); if (i!=0) { - printf ("pj_cart_selftest fails with %d\n", i); + fprintf (T.fout, "pj_cart_selftest fails with %d\n", i); another_failure(); } else @@ -524,7 +547,7 @@ static int builtins (const char *args) { i = pj_horner_selftest (); if (i!=0) { - printf ("pj_horner_selftest fails with %d\n", i); + fprintf (T.fout, "pj_horner_selftest fails with %d\n", i); another_failure(); } else @@ -554,7 +577,7 @@ static PJ_COORD todeg_coord (PJ_COORD a) { /*****************************************************************************/ static PJ_COORD parse_coord (const char *args) { /***************************************************************************** - Attempt to interpret args as a PJ_COORD. +Attempt to interpret args as a PJ_COORD. ******************************************************************************/ int i; const char *endp, *prev = args; @@ -575,7 +598,7 @@ static PJ_COORD parse_coord (const char *args) { /*****************************************************************************/ static int accept (const char *args) { /***************************************************************************** - Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. +Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. ******************************************************************************/ T.a = parse_coord (args); if (T.verbosity > 3) @@ -587,8 +610,8 @@ static int accept (const char *args) { /*****************************************************************************/ static int roundtrip (const char *args) { /***************************************************************************** - Check how far we go from the ACCEPTed point when doing successive - back/forward transformation pairs. +Check how far we go from the ACCEPTed point when doing successive +back/forward transformation pairs. ******************************************************************************/ int ntrips; double d, r, ans; @@ -602,7 +625,6 @@ static int roundtrip (const char *args) { ntrips = (int) (endp==args? 100: fabs(ans)); d = strtod_scaled (endp, 1); d = d==HUGE_VAL? T.tolerance: d; - coo = T.a; /* input ("accepted") values - probably in degrees */ coo = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a; @@ -615,7 +637,7 @@ static int roundtrip (const char *args) { if (0==T.op_ko && T.verbosity < 2) banner (T.operation); fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno); + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); fprintf (T.fout, " roundtrip deviation: %.3f mm, expected: %.3f mm\n", 1000*r, 1000*d); } return another_failure (); @@ -633,7 +655,7 @@ static int expect_message (double d, const char *args) { banner (T.operation); fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno); + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); fprintf (T.fout, " expected: %s\n", args); fprintf (T.fout, " got: %.9f %.9f", T.b.xy.x, T.b.xy.y); if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0) @@ -652,7 +674,7 @@ static int expect_message_cannot_parse (const char *args) { if (0==T.op_ko && T.verbosity < 2) banner (T.operation); fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) lineno, args); + fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) F->lineno, args); } return 1; } @@ -665,7 +687,7 @@ static int expect_failure_with_errno_message (int expected, int got) { if (0==T.op_ko && T.verbosity < 2) banner (T.operation); fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno); + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); fprintf (T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), got, pj_strerrno (got)); fprintf (T.fout, " expected %s (%d): %s", err_const_from_errno(expected), expected, pj_strerrno (expected)); fprintf (T.fout, "\n"); @@ -676,7 +698,7 @@ static int expect_failure_with_errno_message (int expected, int got) { /*****************************************************************************/ static int expect (const char *args) { /***************************************************************************** - Tell GIE what to expect, when transforming the ACCEPTed input +Tell GIE what to expect, when transforming the ACCEPTed input ******************************************************************************/ PJ_COORD ci, co, ce; double d; @@ -737,6 +759,7 @@ static int expect (const char *args) { puts (T.dir== 1? "forward": "reverse"); puts (proj_angular_input (T.P, T.dir)? "angular in": "linear in"); puts (proj_angular_output (T.P, T.dir)? "angular out": "linear out"); + printf ("left: %d right: %d\n", T.P->left, T.P->right); } T.e = parse_coord (args); @@ -753,7 +776,6 @@ static int expect (const char *args) { 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 = proj_trans (T.P, T.dir, ci); T.b = proj_angular_output (T.P, T.dir)? todeg_coord (co): co; @@ -791,7 +813,7 @@ static int expect (const char *args) { /*****************************************************************************/ static int verbose (const char *args) { /***************************************************************************** - Tell the system how noisy it should be +Tell the system how noisy it should be ******************************************************************************/ int i = (int) proj_atof (args); @@ -806,20 +828,11 @@ static int verbose (const char *args) { return 0; } -/*****************************************************************************/ -static int comment (const char *args) { -/***************************************************************************** - in line comment. Equivalent to # -******************************************************************************/ - (void) args; - return 0; -} - /*****************************************************************************/ static int echo (const char *args) { /***************************************************************************** - Add user defined noise to the output stream +Add user defined noise to the output stream ******************************************************************************/ fprintf (T.fout, "%s\n", args); return 0; @@ -828,46 +841,17 @@ fprintf (T.fout, "%s\n", args); static int dispatch (const char *cmnd, const char *args) { -#if 0 - int last_errno = proj_errno_reset (T.P); -#endif - - if (0==level%2) { - if (0==strcmp (cmnd, "BEGIN") || 0==strcmp (cmnd, "") || 0==strcmp (cmnd, "")) - level++; - return 0; - } - - if (0==strcmp (cmnd, "OPERATION")) return operation ((char *) args); if (0==strcmp (cmnd, "operation")) return operation ((char *) args); - if (0==strcmp (cmnd, "ACCEPT")) return accept (args); if (0==strcmp (cmnd, "accept")) return accept (args); - if (0==strcmp (cmnd, "EXPECT")) return expect (args); if (0==strcmp (cmnd, "expect")) return expect (args); - if (0==strcmp (cmnd, "ROUNDTRIP")) return roundtrip (args); if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args); - if (0==strcmp (cmnd, "BANNER")) return banner (args); if (0==strcmp (cmnd, "banner")) return banner (args); - if (0==strcmp (cmnd, "VERBOSE")) return verbose (args); if (0==strcmp (cmnd, "verbose")) return verbose (args); - if (0==strcmp (cmnd, "DIRECTION")) return direction (args); if (0==strcmp (cmnd, "direction")) return direction (args); - if (0==strcmp (cmnd, "TOLERANCE")) return tolerance (args); if (0==strcmp (cmnd, "tolerance")) return tolerance (args); - if (0==strcmp (cmnd, "BUILTINS")) return builtins (args); if (0==strcmp (cmnd, "builtins")) return builtins (args); - if (0==strcmp (cmnd, "ECHO")) return echo (args); if (0==strcmp (cmnd, "echo")) return echo (args); - if (0==strcmp (cmnd, "END")) return finish_previous_operation (args), level++, 0; - if (0==strcmp (cmnd, "")) return finish_previous_operation (args), level++, 0; - if (0==strcmp (cmnd, "")) return finish_previous_operation (args), level++, 0; - if ('#'==cmnd[0]) return comment (args); - -#if 0 - if (proj_errno(T.P)) - printf ("#####***** ERRNO=%d\n", proj_errno(T.P)); - proj_errno_restore (T.P, last_errno); -#endif + return 0; } @@ -941,7 +925,7 @@ static const struct errno_vs_err_const lookup[] = { static const struct errno_vs_err_const unknown = {"PJD_ERR_UNKNOWN", 9999}; -static void list_err_codes (void) { +static int list_err_codes (void) { int i; const int n = sizeof lookup / sizeof lookup[0]; @@ -950,7 +934,7 @@ static void list_err_codes (void) { break; printf ("%25s (%2.2d): %s\n", lookup[i].the_err_const + 8, lookup[i].the_errno, pj_strerrno(lookup[i].the_errno)); } - exit (0); + return 0; } @@ -966,7 +950,6 @@ static const char *err_const_from_errno (int err) { } - static int errno_from_err_const (const char *err_const) { const size_t n = sizeof lookup / sizeof lookup[0]; size_t i, len; @@ -1006,12 +989,6 @@ static int errno_from_err_const (const char *err_const) { } - - - - - - static int errmsg (int errlev, const char *msg, ...) { va_list args; va_start(args, msg); @@ -1022,105 +999,318 @@ static int errmsg (int errlev, const char *msg, ...) { return errlev; } -#define skipspace(f, c) \ - do { \ - while (isspace (c=fgetc(f)) && !feof(f)){ \ - if ('\n'==c) lineno++; \ - } \ - if (feof(f)) \ - break; \ - } while (ungetc(c, f), 0) - -#define skipline(f, c) \ - do { \ - while ((c=fgetc(f)) && !feof(f)) { \ - if ((c=='\r') || (c=='\n')) \ - break; \ - } \ - skipspace (f, c); \ - } while (0) - - -/* skip whitespace at continuation line */ -#define continuation(f, buf, c) \ - if ((c=='\r')||(c=='\n')) { \ - if (c=='\n') lineno++; \ - next--; \ - while (isspace (c=fgetc(f)) && !feof(f)); \ - } -static int get_inp (FILE *f, char *inp, int size) { - char *next; - int c = 0, esc; - char *endp = inp + size - 2; - - skipspace (f, c); - - for (c = esc = 0, next = inp; !feof(f); ) { - c = fgetc(f); - if (esc) { - continuation (f, next, c); - esc = 0; - /* handle escape sequences here */ - switch (c) { - case '\\': c = '\\'; break; - default: (void) c; - } - } - if (c=='\r') - break; - if (c=='\n') { - lineno++; - break; - } - *next++ = (char) c; - if ('\\'==c) - esc = 1; - if (feof(f) || (next==endp)) - break; + + + + + +/**************************************************************************************** + +FFIO - Flexible format I/O + +FFIO provides functionality for reading proj style instruction strings written +in a less strict format than usual: + +* Whitespace is generally allowed everywhere +* Comments can be written inline, '#' style +* ... or as free format blocks + +The overall mission of FFIO is to facilitate communications of geodetic +parameters and test material in a format that is highly human readable, +and provides ample room for comment, documentation, and test material. + +See the PROJ ".gie" test suites for examples of supported formatting. + +****************************************************************************************/ + +#include +#include +#include + +#include +#include + +#include +#include + + + +static int get_inp (ffio *F); +static int skip_to_next_tag (ffio *F); +static int step_into_gie_block (ffio *F); +static int locate_tag (ffio *F, const char *tag); +static int nextline (ffio *F); +static int at_end_delimiter (ffio *F); +static const char *at_tag (ffio *F); +static int at_decorative_element (ffio *F); +static ffio *ffio_destroy (ffio *F); +static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size); + + + +/***************************************************************************************/ +static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size) { +/**************************************************************************************** +Constructor for the ffio object. +****************************************************************************************/ + ffio *G = calloc (1, sizeof (ffio)); + if (0==G) + return 0; + + if (0==max_record_size) + max_record_size = 1000; + + G->args = calloc (1, 5*max_record_size); + if (0==G->args) { + free (G); + return 0; } - *(next) = 0; - return (int) strlen(inp); + + G->next_args = calloc (1, max_record_size); + if (0==G->args) { + free (G->args); + free (G); + return 0; + } + + G->args_size = 5*max_record_size; + G->next_args_size = max_record_size; + + G->tags = tags; + G->n_tags = n_tags; + return G; } -static int get_cmnd (const char *inp, char *cmnd, int len) { - cmnd[0] = 0; - while (isspace(*inp++)); - inp--; - while (len-- && !isspace(*inp) && *inp) - *cmnd++ = *inp++; - *cmnd = 0; - return len; + + +/***************************************************************************************/ +static ffio *ffio_destroy (ffio *G) { +/**************************************************************************************** +Free all allocated associated memory, then free G itself. For extra RAII compliancy, +the file object should also be closed if still open, but this will require additional +control logic, and ffio is a gie tool specific package, so we fall back to asserting that +fclose has been called prior to ffio_destroy. +****************************************************************************************/ + free (G->args); + free (G->next_args); + free (G); + return 0; } -static const char *get_args (const char *inp) { - const char *args = inp; - while (isspace(*args++)) - if (0==*args) - return args; - while (!isspace(*++args)) - if (0==*args) - return args; - while (isspace(*args++)) - if (0==*args) - return args; - return --args; + + +/***************************************************************************************/ +static int at_decorative_element (ffio *G) { +/**************************************************************************************** +A decorative element consists of a line of at least 5 consecutive identical chars, +starting at buffer position 0: +"-----", "=====", "*****", etc. + +A decorative element serves as a end delimiter for the current element, and +continues until a gie command verb is found at the start of a line +****************************************************************************************/ + int i; + char *c; + if (0==G) + return 0; + c = G->next_args; + if (0==c) + return 0; + if (0==c[0]) + return 0; + for (i = 1; i < 5; i++) + if (c[i]!=c[0]) + return 0; + return 1; +} + + + +/***************************************************************************************/ +static const char *at_tag (ffio *G) { +/**************************************************************************************** +A start of a new command serves as an end delimiter for the current command +****************************************************************************************/ + size_t j; + for (j = 0; j < G->n_tags; j++) + if (strncmp (G->next_args, G->tags[j], strlen(G->tags[j]))==0) + return G->tags[j]; + return 0; } +/***************************************************************************************/ +static int at_end_delimiter (ffio *G) { +/**************************************************************************************** +An instruction consists of everything from its introductory tag to its end +delimiter. An end delimiter can be either the introductory tag of the next +instruction, or a "decorative element", i.e. one of the "ascii art" style +block delimiters typically used to mark up block comments in a free format +file. +****************************************************************************************/ + if (G==0) + return 0; + if (at_decorative_element (G)) + return 1; + if (at_tag (G)) + return 1; + return 0; +} +/***************************************************************************************/ +static int nextline (ffio *G) { +/**************************************************************************************** +Read next line of input file. Returns 1 on success, 0 on failure. +****************************************************************************************/ + G->next_args[0] = 0; + if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) + return 0; + if (feof (G->f)) + return 0; + pj_chomp (G->next_args); + G->next_lineno++; + return 1; +} +/***************************************************************************************/ +static int locate_tag (ffio *G, const char *tag) { +/**************************************************************************************** +Find start-of-line tag (currently only used to search for for , but any tag +valid). + +Returns 1 on success, 0 on failure. +****************************************************************************************/ + size_t n = strlen (tag); + while (0!=strncmp (tag, G->next_args, n)) + if (0==nextline (G)) + return 0; + return 1; +} + +/***************************************************************************************/ +static int step_into_gie_block (ffio *G) { +/**************************************************************************************** +Make sure we're inside a -block. Return 1 on success, 0 otherwise. +****************************************************************************************/ + /* Already inside */ + if (G->level % 2) + return 1; + if (0==locate_tag (G, "")) + return 0; + while (0!=strncmp ("", G->next_args, 5)) { + G->next_args[0] = 0; + if (feof (G->f)) + return 0; + if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) + return 0; + pj_chomp (G->next_args); + G->next_lineno++; + } + G->level++; + /* We're ready at the start - now step into the block */ + return nextline (G); +} + + + +/***************************************************************************************/ +static int skip_to_next_tag (ffio *G) { +/**************************************************************************************** +Skip forward to the next command tag. Return 1 on success, 0 otherwise. +****************************************************************************************/ + const char *c; + if (0==step_into_gie_block (G)) + return 0; + + c = at_tag (G); + + /* If not already there - get there */ + while (!c) { + if (0==nextline (G)) + return 0; + c = at_tag (G); + } + + /* If we reached the end of a block, locate the next and retry */ + if (0==strcmp (c, "")) { + G->level++; + if (feof (G->f)) + return 0; + if (0==step_into_gie_block (G)) + return 0; + G->args[0] = 0; + return skip_to_next_tag (G); + } + G->lineno = G->next_lineno; + + return 1; +} + +/* Add the most recently read line of input to the block already stored. */ +static int append_args (ffio *G) { + size_t skip_chars = 0; + size_t next_len = strlen (G->next_args); + size_t args_len = strlen (G->args); + const char *tag = at_tag (G); + + if (tag) + skip_chars = strlen (tag); + + if (G->args_size < args_len + next_len - skip_chars + 1) { + void *p = realloc (G->args, 2 * G->args_size); + if (0==p) + return 0; + G->args = p; + G->args_size = 2 * G->args_size; + } + + G->args[args_len] = ' '; + strcpy (G->args + args_len + 1, G->next_args + skip_chars); + + G->next_args[0] = 0; + return 1; +} + + + + + +/***************************************************************************************/ +static int get_inp (ffio *G) { +/**************************************************************************************** +The primary command reader for gie. Reads a block of gie input, cleans up repeated +whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwise. +****************************************************************************************/ + G->args[0] = 0; + + if (0==skip_to_next_tag (G)) + return 0; + G->tag = at_tag (G); + + if (0==G->tag) + return 0; + + do { + append_args (G); + if (0==nextline (G)) + return 0; + } while (!at_end_delimiter (G)); + + pj_shrink (G->args); + return 1; +} @@ -1171,6 +1361,7 @@ static int pj_horner_selftest (void) { dist = proj_roundtrip (P, PJ_FWD, 1, &c); if (dist > 0.01) return 1; + proj_destroy(P); /* The complex polynomial transformation between the "System Storebaelt" and utm32/ed50 */ P = proj_create (PJ_DEFAULT_CTX, sb_utm32); @@ -1239,7 +1430,7 @@ static int pj_cart_selftest (void) { size_t n, sz; double dist, h, t; char *args[3] = {"proj=utm", "zone=32", "ellps=GRS80"}; - char *arg = {"+proj=utm +zone=32 +ellps=GRS80"}; + const char *arg = {"+proj=utm +zone=32 +ellps=GRS80"}; char buf[40]; /* An utm projection on the GRS80 ellipsoid */ @@ -1279,7 +1470,7 @@ static int pj_cart_selftest (void) { return 3; /* Clear any previous error */ - proj_errno_set (P, 0); + proj_errno_reset (P); /* Invalid projection */ a = proj_trans (P, 42, a); @@ -1290,7 +1481,7 @@ static int pj_cart_selftest (void) { return 5; /* Clear error again */ - proj_errno_set (P, 0); + proj_errno_reset (P); /* Clean up */ proj_destroy (P); @@ -1310,9 +1501,9 @@ static int pj_cart_selftest (void) { b = proj_trans (P, PJ_FWD, a); /* Check roundtrip precision for 10000 iterations each way */ - dist = proj_roundtrip (P, PJ_FWD, 10000, &a); - dist = proj_roundtrip (P, PJ_INV, 10000, &b); - if (dist > 2e-9) + dist = proj_roundtrip (P, PJ_FWD, 10000, &a); + dist += proj_roundtrip (P, PJ_INV, 10000, &b); + if (dist > 4e-9) return 7; @@ -1463,6 +1654,7 @@ static int pj_cart_selftest (void) { /* proj_info() */ /* this one is difficult to test, since the output changes with the setup */ info = proj_info(); + if (info.version[0] != '\0' ) { char tmpstr[64]; sprintf(tmpstr, "%d.%d.%d", info.major, info.minor, info.patch); @@ -1481,6 +1673,7 @@ static int pj_cart_selftest (void) { if ( !pj_info.has_inverse ) { proj_destroy(P); return 61; } if ( strcmp(pj_info.definition, arg) ) { proj_destroy(P); return 62; } if ( strcmp(pj_info.id, "utm") ) { proj_destroy(P); return 63; } + proj_destroy(P); /* proj_grid_info() */ @@ -1500,7 +1693,6 @@ static int pj_cart_selftest (void) { if ( strcmp(init_info.name, "epsg") ) return 68; - /* test proj_rtodms() and proj_dmstor() */ if (strcmp("180dN", proj_rtodms(buf, M_PI, 'N', 'S'))) return 70; @@ -1616,14 +1808,6 @@ static int pj_cart_selftest (void) { - - - - - - - - static int test_time(const char* args, double tol, double t_in, double t_exp) { PJ_COORD in, out; PJ *P = proj_create(PJ_DEFAULT_CTX, args); @@ -1706,10 +1890,10 @@ static int pj_unitconvert_selftest (void) { ret = test_time(args3, 1e-6, in3, in3); if (ret) return ret + 30; ret = test_time(args4, 1e-6, in4, exp4); if (ret) return ret + 40; ret = test_xyz (args5, 1e-10, in5, exp5); if (ret) return ret + 50; + ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 50; ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 60; ret = test_time(args7, 1e-6, in7, in7); if (ret) return ret + 70; return 0; } - diff --git a/src/makefile.vc b/src/makefile.vc index ef460719..1155035e 100644 --- a/src/makefile.vc +++ b/src/makefile.vc @@ -159,6 +159,8 @@ install: all -mkdir $(INSTDIR)\share -mkdir $(INSTDIR)\lib -mkdir $(INSTDIR)\include + -mkdir $(INSTDIR)\test + -mkdir $(INSTDIR)\test\gie copy *.exe $(INSTDIR)\bin copy *.dll $(INSTDIR)\bin copy *.lib $(INSTDIR)\lib @@ -166,3 +168,4 @@ install: all copy proj_api.h $(INSTDIR)\include copy projects.h $(INSTDIR)\include copy geodesic.h $(INSTDIR)\include + copy ..\test\gie\*.gie $(INSTDIR)\test\gie diff --git a/src/optargpm.h b/src/optargpm.h index acb96583..97755cdb 100644 --- a/src/optargpm.h +++ b/src/optargpm.h @@ -183,15 +183,12 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-09-10 * DEALINGS IN THE SOFTWARE. ***********************************************************************/ - -#define PJ_LIB__ -#include +#include +#include +#include #include #include -#include #include -#include -#include /**************************************************************************************************/ struct OPTARGS; @@ -219,6 +216,7 @@ struct OPTARGS { FILE *input; int input_index; int record_index; + int free_format; /* plus-style specs replaced by free format */ const char *progname; /* argv[0], stripped from /path/to, if present */ char flaglevel[21]; /* if flag -f is specified n times, its optarg pointer is set to flaglevel + n */ char *optarg[256]; /* optarg[(int) 'f'] holds a pointer to the argument of option "-f" */ @@ -426,6 +424,12 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, o->argc = argc; o->argv = argv; o->progname = opt_strip_path (argv[0]); + o->free_format = 0; + + /* Is free format in use, instead of plus-style? */ + for (i = 1; i < argc; i++) + if (0==strcmp ("--", argv[i])) + o->free_format = i; /* Reset all flags */ for (i = 0; i < (int) strlen (flags); i++) @@ -442,7 +446,8 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, o->longkeys = longkeys; - /* check aliases, An end user should never experience this, but the developer should make sure that aliases are valid */ + /* check aliases, An end user should never experience this, but */ + /* the developer should make sure that aliases are valid */ for (i = 0; longflags && longflags[i]; i++) { /* Go on if it does not look like an alias */ if (strlen (longflags[i]) < 3) @@ -500,6 +505,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, if ('-' != argv[i][0]) break; + if (0==o->margv) o->margv = argv + i; o->margc++; @@ -516,7 +522,10 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, char *equals; crepr = argv[i] + 2; - /* need to maniplulate a bit to support gnu style --pap=pop syntax */ + /* We need to manipulate a bit to support gnu style --foo=bar syntax. */ + /* NOTE: This will segfault for read-only (const char * style) storage, */ + /* but since the canonical use case, int main (int argc, char **argv), */ + /* is non-const, we ignore this for now */ equals = strchr (crepr, '='); if (equals) *equals = 0; @@ -579,6 +588,14 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, /* Process all '+'-style options, starting from where '-'-style processing ended */ o->pargv = argv + i; + if (o->free_format) { + o->pargc = o->free_format - (o->margc + 1); + o->fargc = argc - (o->free_format + 1); + if (0 != o->fargc) + o->fargv = argv + o->free_format + 1; + return o; + } + for (/* empty */; i < argc; i++) { if ('-' == argv[i][0]) { free (o); diff --git a/src/pj_factors.c b/src/pj_factors.c index 31c0e539..6ba993b9 100644 --- a/src/pj_factors.c +++ b/src/pj_factors.c @@ -17,12 +17,19 @@ int pj_factors(LP lp, const PJ *P, double h, struct FACTORS *fac) { PJ_COORD coo = {{0, 0, 0, 0}}; coo.lp = lp; - err = proj_errno_reset (P); + /* Failing the 3 initial checks will most likely be due to */ + /* earlier errors, so we leave errno alone */ + if (0==fac) + return 1; - if (0==fac) { - proj_errno_set (P, ENOMEM); + if (0==P) return 1; - } + + if (HUGE_VAL==lp.lam) + return 1; + + /* But from here, we're ready to make our own mistakes */ + err = proj_errno_reset (P); /* Indicate that all factors are numerical approximations */ fac->code = 0; diff --git a/src/pj_init.c b/src/pj_init.c index 2d588ee4..8f4eb477 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -27,320 +27,352 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ + + #define PJ_LIB__ #include #include #include #include #include +#include #include "proj_internal.h" #include "projects.h" -/* Maximum size of files using the "escape carriage return" feature */ -#define MAX_CR_ESCAPE 65537 -typedef struct { - projCtx ctx; - PAFile fid; - char buffer[MAX_CR_ESCAPE]; - int buffer_filled; - int at_eof; -} pj_read_state; -/************************************************************************/ -/* fill_buffer() */ -/************************************************************************/ -static const char *fill_buffer(pj_read_state *state, const char *last_char) -{ - size_t bytes_read; - size_t char_remaining, char_requested; - char *r, *w; - -/* -------------------------------------------------------------------- */ -/* Don't bother trying to read more if we are at eof, or if the */ -/* buffer is still over half full. */ -/* -------------------------------------------------------------------- */ - if (last_char == NULL) - last_char = state->buffer; - - if (state->at_eof) - return last_char; - - char_remaining = state->buffer_filled - (last_char - state->buffer); - if (char_remaining >= sizeof(state->buffer) / 2) - return last_char; - -/* -------------------------------------------------------------------- */ -/* Move the existing data to the start of the buffer. */ -/* -------------------------------------------------------------------- */ - memmove(state->buffer, last_char, char_remaining); - state->buffer_filled = (int)char_remaining; - last_char = state->buffer; - -/* -------------------------------------------------------------------- */ -/* Refill. */ -/* -------------------------------------------------------------------- */ - char_requested = sizeof(state->buffer) - state->buffer_filled - 1; - bytes_read = pj_ctx_fread( state->ctx, state->buffer + state->buffer_filled, - 1, char_requested, state->fid ); - if (bytes_read < char_requested) - { - state->at_eof = 1; - state->buffer[state->buffer_filled + bytes_read] = '\0'; - } +/**************************************************************************************/ +static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) { +/*************************************************************************************** + Convert a string (presumably originating from get_init_string) to a paralist. +***************************************************************************************/ + char *c = definition; + paralist *first = 0, *next = 0; -/* -------------------------------------------------------------------- */ -/* Line continuations: skip whitespace after escaped newlines */ -/* -------------------------------------------------------------------- */ - r = state->buffer; - w = state->buffer; - while (*r) { - /* Escaped newline? */ - while ((r[0]=='\\') && ((r[1]=='\n') || (r[1]=='\r'))) { - r += 2; - while (isspace (*r)) - r++; - /* we also skip comments immediately after an escaped newline */ - while (*r=='#') { - while( *r && (*r != '\n') ) - r++; - while (isspace (*r)) - r++; - /* Reaching end of buffer while skipping continuation comment is currently an error */ - if (0==*r) { - pj_ctx_set_errno (state->ctx, -2); - pj_log (state->ctx, PJ_LOG_ERROR, "init file too big"); - return 0; - } - } - } - *w++ = *r++; + while (*c) { + /* Find start of next substring */ + while (isspace (*c)) + c++; + + /* Keep a handle to the start of the list, so we have something to return */ + if (0==first) + first = next = pj_mkparam_ws (c); + else + next = next->next = pj_mkparam_ws (c); + if (0==next) + return pj_dealloc_params (ctx, first, ENOMEM); + + /* And skip to the end of the substring */ + while ((!isspace(*c)) && 0!=*c) + c++; } - *w = 0; - state->buffer_filled += (int)(bytes_read - (r-w)); - return last_char; + /* Terminate list and return */ + next->next = 0; + return first; } -/************************************************************************/ -/* get_opt() */ -/************************************************************************/ -static paralist * -get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, - int *found_def) { - pj_read_state *state = (pj_read_state*) calloc(1,sizeof(pj_read_state)); - char sword[MAX_CR_ESCAPE]; - int len; - int in_target = 0; - const char *next_char = NULL; - state->fid = fid; - state->ctx = ctx; - next_char = fill_buffer(state, NULL); - if(found_def) - *found_def = 0; - - len = (int)strlen(name); - *sword = 't'; - - if (0==next_char) + + +/**************************************************************************************/ +static char *get_init_string (PJ_CONTEXT *ctx, char *name) { +/*************************************************************************************** + Read a section of an init file. Return its contents as a plain character string. + It is the duty of the caller to free the memory allocated for the string. +***************************************************************************************/ +#define MAX_LINE_LENGTH 1000 + size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1); + char *fname, *section, *key; + char *buffer = 0; + char *line = 0; + PAFile fid; + size_t n; + + + line = pj_malloc (MAX_LINE_LENGTH + 1); + if (0==line) return 0; - /* loop till we find our target keyword */ - while (*next_char) - { - next_char = fill_buffer(state, next_char); + fname = pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3); + if (0==fname) { + pj_dealloc (line); + return 0; + } + + /* Support "init=file:section", "+init=file:section", and "file:section" format */ + key = strstr (name, "init="); + if (0==key) + key = name; + else + key += 5; + if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) { + pj_dealloc (fname); + pj_dealloc (line); + return 0; + } + memmove (fname, key, strlen (key) + 1); + + /* Locate the name of the section we search for */ + section = strrchr(fname, ':'); + if (0==section) { + proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING); + pj_dealloc (fname); + pj_dealloc (line); + return 0; + } + *section = 0; + section++; + n = strlen (section); + pj_log (ctx, 3, "get_init_string: searching for section [%s] in init file [%s]\n", section, fname); + + fid = pj_open_lib (ctx, fname, "rt"); + if (0==fid) { + pj_dealloc (fname); + pj_dealloc (line); + proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); + return 0; + } - /* Skip white space. */ - while( isspace(*next_char) ) - next_char++; + /* Search for section in init file */ + for (;;) { - next_char = fill_buffer(state, next_char); - if (0==next_char) + /* End of file? */ + if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) { + pj_dealloc (buffer); + pj_dealloc (fname); + pj_dealloc (line); + pj_ctx_fclose (ctx, fid); + proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); return 0; + } - /* for comments, skip past end of line. */ - if( *next_char == '#' ) - { - while( *next_char && *next_char != '\n' ) - next_char++; + /* At start of right section? */ + pj_chomp (line); + if ('<'!=line[0]) + continue; + if (strlen (line) < n + 2) + continue; + if (line[n + 1] != '>') + continue; + if (0==strncmp (line + 1, section, n)) + break; + } - next_char = fill_buffer(state, next_char); - if (0==next_char) - return 0; - if (*next_char == '\n') - next_char++; - if (*next_char == '\r') - next_char++; + /* We're at the first line of the right section - copy line to buffer */ + buffer = pj_malloc (current_buffer_size); + if (0==buffer) { + pj_dealloc (fname); + pj_dealloc (line); + pj_ctx_fclose (ctx, fid); + return 0; + } - } + /* Skip the "
" indicator, and copy the rest of the line over */ + strcpy (buffer, line + strlen (section) + 2); - /* Is this our target? */ - else if( *next_char == '<' ) - { - /* terminate processing target on the next block definition */ - if (in_target) - break; + /* Copy the remaining lines of the section to buffer */ + for (;;) { + char *end_i_cator; + size_t next_length, buffer_length; - next_char++; - if (strncmp(name, next_char, len) == 0 - && next_char[len] == '>') - { - /* skip past target word */ - next_char += len + 1; - in_target = 1; - if(found_def) - *found_def = 1; - } - else - { - /* skip past end of line */ - while( *next_char && *next_char != '\n' ) - next_char++; - } + /* Did the section end somewhere in the most recently read line? */ + end_i_cator = strchr (buffer, '<'); + if (end_i_cator) { + *end_i_cator = 0; + break; } - else if (in_target) - { - const char *start_of_word = next_char; - int word_len = 0; - if (*start_of_word == '+') - { - start_of_word++; - next_char++; - } + /* End of file? - done! */ + if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) + break; - /* capture parameter */ - while ( *next_char && !isspace(*next_char) ) - { - next_char++; - word_len++; + /* Otherwise, handle the line. It MAY be the start of the next section, */ + /* but that will be handled at the start of next trip through the loop */ + buffer_length = strlen (buffer); + next_length = strlen (line) + buffer_length + 2; + if (next_length > current_buffer_size) { + char *b = pj_malloc (2 * current_buffer_size); + if (0==b) { + pj_dealloc (buffer); + buffer = 0; + break; } + strcpy (b, buffer); + current_buffer_size *= 2; + pj_dealloc (buffer); + buffer = b; + } + buffer[buffer_length] = ' '; + strcpy (buffer + buffer_length + 1, line); + } - strncpy(sword+1, start_of_word, word_len); - sword[word_len+1] = '\0'; + pj_ctx_fclose (ctx, fid); + pj_dealloc (fname); + pj_dealloc (line); + if (0==buffer) + return 0; + pj_shrink (buffer); + pj_log (ctx, 3, "key=%s, value: [%s]\n", key, buffer); + return buffer; +} - /* do not override existing parameter value of same name */ - if (!pj_param(ctx, *start, sword).i) { - /* don't default ellipse if datum, ellps or any earth model information is set */ - if (0==strncmp(sword,"tellps=", 7)) { - int n = 0; - n += pj_param(ctx, *start, "tdatum").i; - n += pj_param(ctx, *start, "tellps").i; - n += pj_param(ctx, *start, "ta").i; - n += pj_param(ctx, *start, "tb").i; - n += pj_param(ctx, *start, "trf").i; - n += pj_param(ctx, *start, "tf").i; - n += pj_param(ctx, *start, "te").i; - n += pj_param(ctx, *start, "tes").i; +/************************************************************************/ +static paralist *get_init(PJ_CONTEXT *ctx, char *key) { +/************************************************************************* +Expand key from buffer or (if not in buffer) from init file +*************************************************************************/ + char *xkey, *definition; + paralist *init_items = 0; + + /* support "init=file:section", "+init=file:section", and "file:section" format */ + xkey = strstr (key, "init="); + if (0==xkey) + xkey = key; + else + xkey += 5; + pj_log (ctx, 3, "get_init: searching cache for key: [%s]\n", xkey); + + /* Is file/key pair already in cache? */ + init_items = pj_search_initcache (xkey); + if (init_items) + return init_items; + + /* If not, we must read it from file */ + pj_log (ctx, 3, "get_init: searching on in init files for [%s]\n", xkey); + definition = get_init_string (ctx, xkey); + if (0==definition) + return 0; + init_items = string_to_paralist (ctx, definition); + pj_log (ctx, 3, "get_init: got [%s], paralist[0,1]: [%s,%s]\n", definition, init_items->param, init_items->next? init_items->next->param: "(empty)"); + pj_dealloc (definition); + if (0==init_items) + return 0; - if (0==n) - next = next->next = pj_mkparam(sword+1); - } - else - next = next->next = pj_mkparam(sword+1); - } + /* We found it in file - now insert into the cache, before returning */ + pj_insert_initcache (xkey, init_items); + return init_items; +} - } - else - { - /* skip past word */ - while( *next_char && !isspace(*next_char) ) { - next_char++; - } - } - } +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 (errno == 25) - errno = 0; + if (0==start) + return 0; - free(state); - return next; -} + if (strlen(key) > ID_TAG_MAX) + return 0; -/************************************************************************/ -/* get_defaults() */ -/************************************************************************/ -static paralist *get_defaults(projCtx ctx, paralist **start, paralist *next, char *name) { - PAFile fid; + /* 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 ( (fid = pj_open_lib(ctx,"proj_def.dat", "rt")) != NULL) { - next = get_opt(ctx, start, fid, "general", next, NULL); - pj_ctx_fseek(ctx, fid, 0, SEEK_SET); - next = get_opt(ctx, start, fid, name, next, NULL); - pj_ctx_fclose(ctx, fid); + /* If we're here, it's OK to append the current default item */ + last = last->next = pj_mkparam(next->param); } - if (errno) - errno = 0; /* don't care if can't open file */ - ctx->last_errno = 0; + last->next = 0; - return next; + pj_dealloc_params (ctx, defaults, 0); + return last; } -/************************************************************************/ -/* get_init() */ -/************************************************************************/ -static paralist *get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) { - char fname[MAX_PATH_FILENAME+ID_TAG_MAX+3], *opt; - PAFile fid; - paralist *init_items = NULL; - const paralist *orig_next = next; +/*****************************************************************************/ +paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) { +/****************************************************************************** +Append expansion of to the paralist . The expansion is appended, +rather than inserted at 's place, since may contain +overrides to the expansion. These must take precedence, and hence come first +in the expanded list. - (void)strncpy(fname, name, sizeof(fname)-2); - fname[sizeof(fname)-2] = '\0'; +Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm +zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid. - /* - ** Search for file/key pair in cache - */ +The expression 'init=foo:bar ellps=intl' will then expand to: - init_items = pj_search_initcache( name ); - if( init_items != NULL ) - { - next->next = init_items; - while( next->next != NULL ) - next = next->next; - *found_def = 1; - return next; - } + 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80', - /* - ** Otherwise we try to open the file and search for it. - */ - if ((opt = strrchr(fname, ':')) != NULL) - *opt++ = '\0'; - else { pj_ctx_set_errno(ctx,-3); return NULL; } +where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence, +turning the expansion into an UTM projection on the Hayford ellipsoid. - if ( (fid = pj_open_lib(ctx,fname, "rt")) != NULL) - next = get_opt(ctx, start, fid, opt, next, found_def); - else - return NULL; +Note that 'init=foo:bar' stays in the list. It is ignored after expansion. +******************************************************************************/ + paralist *last; + paralist *expn; - pj_ctx_fclose(ctx, fid); - if (errno == 25) - errno = 0; /* unknown problem with some sys errno<-25 */ + /* Nowhere to start? */ + if (0==init) + return 0; - /* - ** If we seem to have gotten a result, insert it into the - ** init file cache. - */ - if( next != NULL && next != orig_next ) - pj_insert_initcache( name, orig_next->next ); + expn = get_init(ctx, init->param); - return next; -} + /* Nothing in expansion? */ + if (0==expn) + return 0; -paralist * pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) { - return get_init(ctx, start, next, name, found_def); + /* Locate the end of the list */ + for (last = init; last && last->next; last = last->next); + + /* Then append and return */ + last->next = expn; + return init; } + + /************************************************************************/ /* pj_init_plus() */ /* */ /* Same as pj_init() except it takes one argument string with */ -/* individual arguments preceded by '+', such as "+proj=utm */ +/* individual arguments preceded by '+', such as "+proj=utm */ /* +zone=11 +ellps=WGS84". */ /************************************************************************/ @@ -434,15 +466,27 @@ pj_init(int argc, char **argv) { return pj_init_ctx( pj_get_default_ctx(), argc, argv ); } + +typedef PJ *(constructor)(PJ *); +typedef constructor *(*function_returning_constructor)(const char *); + +static constructor *pj_constructor (const char *name) { + int i; + char *s; + for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ; + if (0==s) + return 0; + return (constructor *) pj_list[i].proj; +} + + PJ * pj_init_ctx(projCtx ctx, int argc, char **argv) { char *s, *name; - paralist *start = NULL; - PJ *(*proj)(PJ *); - paralist *curr; + constructor *proj; + paralist *curr, *init, *start; int i; int err; - int found_def = 0; PJ *PIN = 0; int n_pipelines = 0; int n_inits = 0; @@ -451,7 +495,6 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { ctx = pj_get_default_ctx (); ctx->last_errno = 0; - start = NULL; if (argc <= 0) { pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS); @@ -478,6 +521,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { return 0; } + /* put arguments into internal linked list */ start = curr = pj_mkparam(argv[0]); if (!curr) @@ -490,41 +534,45 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { 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 +inits are expanded as late as possible. */ - if (pj_param(ctx, start, "tinit").i && n_pipelines == 0) { - found_def = 0; - curr = get_init(ctx, &start, curr, pj_param(ctx, start, "sinit").s, &found_def); - if (!curr) + + /* 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 (!found_def) - return pj_dealloc_params (ctx, start, PJD_ERR_NO_OPTION_IN_INIT_FILE); } - if (ctx->last_errno) return pj_dealloc_params (ctx, start, ctx->last_errno); - /* find projection selection */ - if (!(name = pj_param(ctx, start, "sproj").s)) + /* Find projection selection */ + curr = pj_param_exists (start, "proj"); + if (0==curr) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); - for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ; + name = curr->param; + if (strlen (name) < 6) + return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); + name += 5; - if (!s) + proj = pj_constructor (name); + if (0==proj) return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); - /* set defaults, unless inhibited or we are initializing a pipeline */ - if (!(pj_param(ctx, start, "bno_defs").i) && n_pipelines == 0) - curr = get_defaults(ctx,&start, curr, name); - proj = (PJ *(*)(PJ *)) pj_list[i].proj; + /* 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 */ + /* 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; @@ -539,7 +587,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { PIN->vgridlist_geoid = NULL; PIN->vgridlist_geoid_count = 0; - /* set datum parameters */ + /* Set datum parameters */ if (pj_datum_set(ctx, start, PIN)) return pj_default_destructor (PIN, proj_errno(PIN)); @@ -566,18 +614,18 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { PIN->datum_type = PJD_WGS84; } - /* set PIN->geoc coordinate system */ + /* Set PIN->geoc coordinate system */ PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i); - /* over-ranging flag */ + /* Over-ranging flag */ PIN->over = pj_param(ctx, start, "bover").i; - /* vertical datum geoid grids */ + /* 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 */ + /* 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; @@ -588,7 +636,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); } - /* axis orientation */ + /* Axis orientation */ if( (pj_param(ctx, start,"saxis").s) != NULL ) { static const char *axis_legal = "ewnsud"; @@ -601,23 +649,23 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { || strchr( axis_legal, axis_arg[2] ) == NULL) return pj_default_destructor (PIN, PJD_ERR_AXIS); - /* it would be nice to validate we don't have on axis repeated */ + /* TODO: it would be nice to validate we don't have on axis repeated */ strcpy( PIN->axis, axis_arg ); } - /* central meridian */ + /* Central meridian */ PIN->lam0=pj_param(ctx, start, "rlon_0").f; - /* central latitude */ + /* Central latitude */ PIN->phi0 = pj_param(ctx, start, "rlat_0").f; - /* false easting and northing */ + /* 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 */ + /* 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) @@ -627,7 +675,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { if (PIN->k0 <= 0.) return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO); - /* set units */ + /* 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) ; @@ -655,7 +703,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { } else PIN->to_meter = PIN->fr_meter = 1.; - /* set vertical units */ + /* 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) ; @@ -675,7 +723,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { PIN->vfr_meter = PIN->fr_meter; } - /* prime meridian */ + /* Prime meridian */ s = 0; if ((name = pj_param(ctx, start, "spm").s) != NULL) { const char *value = NULL; @@ -708,7 +756,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { return pj_default_destructor (PIN, ENOMEM); geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es))); - /* projection specific initialization */ + /* Projection specific initialization */ err = proj_errno_reset (PIN); PIN = proj(PIN); if (proj_errno (PIN)) { @@ -719,6 +767,8 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { return PIN; } + + /************************************************************************/ /* pj_free() */ /* */ diff --git a/src/pj_internal.c b/src/pj_internal.c index 8a5d2d15..7bfd192b 100644 --- a/src/pj_internal.c +++ b/src/pj_internal.c @@ -33,6 +33,7 @@ #include "projects.h" #include +#include #include #include #include @@ -114,14 +115,15 @@ void proj_context_inherit (PJ *parent, PJ *child) { -size_t pj_strlcpy(char *dst, const char *src, size_t siz) { -/******************************************************************* +/**************************************************************************************/ +size_t pj_strlcpy(char *dst, const char *src, size_t dsize) { +/*************************************************************************************** Copy src to string dst of size siz. At most siz-1 characters will be copied. Always NUL terminates (unless siz == 0). Returns strlen(src); if retval >= siz, truncation occurred. - - * Copyright (c) 1998 Todd C. Miller + * + * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -134,40 +136,243 @@ size_t pj_strlcpy(char *dst, const char *src, size_t siz) { * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * - Source: http://www.i-pi.com/Training/EthicalHacking/Solutions/strlcpy.c + Source: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c -********************************************************************/ - register char *d = dst; - register const char *s = src; - register size_t n = siz; +***************************************************************************************/ + const char *osrc = src; + size_t nleft = dsize; - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') break; - } while (--n != 0); + } } - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) ; } - return(s - src - 1); /* count does not include NUL */ + return(src - osrc - 1); /* count does not include NUL */ } -/* stuff below is *not* considered API, and will be moved to an "internal plumbing toolset" */ +/*****************************************************************************/ +char *pj_chomp (char *c) { +/****************************************************************************** +Strip pre- and postfix whitespace. Inline comments (indicated by '#') are +considered whitespace. +******************************************************************************/ + size_t i, n; + char *comment; + char *start = c; + + if (0==c) + return 0; + + comment = strchr (c, '#'); + if (comment) + *comment = 0; + + n = strlen (c); + if (0==n) + return c; + + /* Eliminate postfix whitespace */ + for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--) + c[i] = 0; + + /* Find start of non-whitespace */ + while (0 != *start && (';'==*start || isspace (*start))) + start++; + + n = strlen (start); + if (0==n) { + c[0] = 0; + return c; + } + + memmove (c, start, n + 1); + return c; +} +/*****************************************************************************/ +char *pj_shrink (char *c) { +/****************************************************************************** +Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy, +consuming their surrounding whitespace. +******************************************************************************/ + size_t i, j, n; + + /* Flag showing that a whitespace (ws) has been written after last non-ws */ + size_t ws; + + if (0==c) + return 0; + + pj_chomp (c); + n = strlen (c); + + /* First collapse repeated whitespace (including +/;) */ + for (i = j = 0, ws = 0; j < n; j++) { + + /* Eliminate prefix '+', only if preceeded by whitespace */ + /* (i.e. keep it in 1.23e+08) */ + if ((i > 0) && ('+'==c[j]) && isspace (c[i])) + c[j] = ' '; + + if (isspace (c[j]) || ';'==c[j]) { + if (0==ws && (i > 0)) + c[i++] = ' '; + ws = 1; + continue; + } + else { + ws = 0; + c[i++] = c[j]; + } + } + c[i] = 0; + n = strlen(c); + + /* Then make ',' and '=' greedy */ + for (i = j = 0; j < n; j++) { + if (i==0) { + c[i++] = c[j]; + continue; + } + + /* Skip space before '='/',' */ + if ('='==c[j] || ','==c[j]) { + if (c[i - 1]==' ') + c[i - 1] = c[j]; + else + c[i++] = c[j]; + continue; + } + + if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) ) + continue; + + c[i++] = c[j]; + } + c[i] = 0; + return c; +} + + + +/*****************************************************************************/ +size_t pj_trim_argc (char *args) { +/****************************************************************************** +Trim all unnecessary whitespace (and non-essential syntactic tokens) from the +argument string, args, and count its number of elements. +******************************************************************************/ + size_t i, m, n; + pj_shrink (args); + n = strlen (args); + if (n==0) + return 0; + for (i = m = 0; i < n; i++) { + if (' '==args[i]) { + args[i] = 0; + m++; + } + } + return m + 1; +} + + + +/*****************************************************************************/ +char **pj_trim_argv (size_t argc, char *args) { +/****************************************************************************** +Create an argv-style array from elements placed in the argument string, args. + +args is a trimmed string as returned by pj_trim_argc(), and argc is the number +of trimmed strings found (i.e. the return value of pj_trim_args()). Hence, + int argc = pj_trim_argc (args); + char **argv = pj_trim_argv (argc, args); +will produce a classic style (argc, argv) pair from a string of whitespace +separated args. No new memory is allocated for storing the individual args +(they stay in the args string), but for the pointers to the args a new array +is allocated and returned. + +It is the duty of the caller to free this array. +******************************************************************************/ + size_t i, j; + char **argv; + + if (0==args) + return 0; + if (0==argc) + return 0; + + + /* turn the input string into an array of strings */ + argv = (char **) calloc (argc, sizeof (char *)); + if (0==argv) + return 0; + argv[0] = args; + for (i = 0, j = 1; ; i++) { + if (0==args[i]) { + argv[j++] = args + (i + 1); + } + if (j==argc) + break; + } + return argv; +} + + + +/*****************************************************************************/ +char *pj_make_args (size_t argc, char **argv) { +/****************************************************************************** +pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It +converts free format command line input to something proj_create can consume. + +Allocates, and returns, an array of char, large enough to hold a whitespace +separated copy of the args in argv. It is the duty of the caller to free this +array. +******************************************************************************/ + size_t i, n; + char *p; + + for (i = n = 0; i < argc; i++) + n += strlen (argv[i]); + + p = pj_calloc (n + argc + 1, sizeof (char)); + if (0==p) + return 0; + if (0==argc) + return p; + + for (i = n = 0; i < argc; i++) { + strcat (p, argv[i]); + strcat (p, " "); + } + return pj_shrink (p); +} + + + +/*****************************************************************************/ void proj_context_errno_set (PJ_CONTEXT *ctx, int err) { +/****************************************************************************** +Raise an error directly on a context, without going through a PJ belonging +to that context. +******************************************************************************/ if (0==ctx) ctx = pj_get_default_ctx(); pj_ctx_set_errno (ctx, err); @@ -176,8 +381,20 @@ void proj_context_errno_set (PJ_CONTEXT *ctx, int err) { -/* Set logging level 0-3. Higher number means more debug info. 0 turns it off */ + + + +/* logging */ + +/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */ +void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ); + + +/***************************************************************************************/ enum proj_log_level proj_log_level (PJ_CONTEXT *ctx, enum proj_log_level log_level) { +/**************************************************************************************** + Set logging level 0-3. Higher number means more debug info. 0 turns it off +****************************************************************************************/ enum proj_log_level previous; if (0==ctx) ctx = pj_get_default_ctx(); @@ -191,35 +408,48 @@ enum proj_log_level proj_log_level (PJ_CONTEXT *ctx, enum proj_log_level log_lev } - -/* logging */ - -/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */ -void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ); - +/*****************************************************************************/ void proj_log_error (PJ *P, const char *fmt, ...) { +/****************************************************************************** + For reporting the most severe events. +******************************************************************************/ va_list args; va_start( args, fmt ); pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args); va_end( args ); } + +/*****************************************************************************/ void proj_log_debug (PJ *P, const char *fmt, ...) { +/****************************************************************************** + For reporting debugging information. +******************************************************************************/ va_list args; va_start( args, fmt ); pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args); va_end( args ); } + +/*****************************************************************************/ void proj_log_trace (PJ *P, const char *fmt, ...) { +/****************************************************************************** + For reporting embarrasingly detailed debugging information. +******************************************************************************/ va_list args; va_start( args, fmt ); pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args); va_end( args ); } -/* Put a new logging function into P's context. The opaque object app_data is passed as first arg at each call to the logger */ + +/*****************************************************************************/ void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION log) { +/****************************************************************************** + Put a new logging function into P's context. The opaque object app_data is + passed as first arg at each call to the logger +******************************************************************************/ if (0==ctx) pj_get_default_ctx (); if (0==ctx) diff --git a/src/pj_malloc.c b/src/pj_malloc.c index c9275074..63278a56 100644 --- a/src/pj_malloc.c +++ b/src/pj_malloc.c @@ -40,6 +40,7 @@ ** projection system memory allocation/deallocation call with custom ** application procedures. */ +#include #include "projects.h" #include @@ -143,7 +144,7 @@ char *pj_strdup(const char *str) /*****************************************************************************/ -void *pj_dealloc_params (projCtx ctx, paralist *start, int errlev) { +void *pj_dealloc_params (PJ_CONTEXT *ctx, paralist *start, int errlev) { /***************************************************************************** Companion to pj_default_destructor (below). Deallocates a linked list of "+proj=xxx" initialization parameters. diff --git a/src/pj_param.c b/src/pj_param.c index ee952eca..5024346d 100644 --- a/src/pj_param.c +++ b/src/pj_param.c @@ -1,20 +1,79 @@ /* put parameters in linked list and retrieve */ -#include +#include #include #include +#include "projects.h" + /* create parameter list entry */ paralist *pj_mkparam(char *str) { - paralist *newitem; - - if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) { - newitem->used = 0; - newitem->next = 0; - if (*str == '+') - ++str; - (void)strcpy(newitem->param, str); - } - return newitem; + paralist *newitem; + + if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) { + newitem->used = 0; + newitem->next = 0; + if (*str == '+') + ++str; + (void)strcpy(newitem->param, str); + } + return newitem; +} + + +/* As pj_mkparam, but payload ends at first whitespace, rather than at end of */ +paralist *pj_mkparam_ws (char *str) { + paralist *newitem; + size_t len = 0; + + if (0==str) + return 0; + + /* Find start and length of string */ + while (isspace (*str)) + str++; + while ((!isspace(str[len])) && 0!=str[len]) + len++; + if (*str == '+') { + str++; + len--; + } + + /* Use calloc to automagically 0-terminate the copy */ + newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len); + if (0==newitem) + return 0; + memmove(newitem->param, str, len); + + newitem->used = 0; + newitem->next = 0; + + return newitem; +} + +/**************************************************************************************/ +paralist *pj_param_exists (paralist *list, const char *parameter) { +/*************************************************************************************** + Determine whether a given parameter exists in a paralist. If it does, return + a pointer to the corresponding list element - otherwise return 0. + + This function is equivalent to the pj_param (...) call with the "opt" argument + set to the parameter name preceeeded by a 't'. But by using this one, one avoids + writing the code allocating memory for a new copy of parameter name, and prepending + the t (for compile time known names, this is obviously not an issue). +***************************************************************************************/ + paralist *next = list; + char *c = strchr (parameter, '='); + size_t len = strlen (parameter); + if (c) + len = c - parameter; + if (list==0) + return 0; + + for (next = list; next; next = next->next) + if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) + return next; + + return 0; } @@ -35,76 +94,76 @@ paralist *pj_mkparam(char *str) { /* */ /************************************************************************/ - PROJVALUE /* test for presence or get parameter value */ + PROJVALUE /* test for presence or get parameter value */ pj_param(projCtx ctx, paralist *pl, const char *opt) { - int type; - unsigned l; - PROJVALUE value; - - if( ctx == NULL ) - ctx = pj_get_default_ctx(); - - type = *opt++; - /* simple linear lookup */ - l = (int)strlen(opt); - while (pl && !(!strncmp(pl->param, opt, l) && - (!pl->param[l] || pl->param[l] == '='))) - pl = pl->next; - if (type == 't') - value.i = pl != 0; - else if (pl) { - pl->used |= 1; - opt = pl->param + l; - if (*opt == '=') - ++opt; - switch (type) { - case 'i': /* integer input */ - value.i = atoi(opt); - break; - case 'd': /* simple real input */ - value.f = pj_atof(opt); - break; - case 'r': /* degrees input */ - value.f = dmstor_ctx(ctx, opt, 0); - break; - case 's': /* char string */ + int type; + unsigned l; + PROJVALUE value; + + if( ctx == NULL ) + ctx = pj_get_default_ctx(); + + type = *opt++; + /* simple linear lookup */ + l = (int)strlen(opt); + while (pl && !(!strncmp(pl->param, opt, l) && + (!pl->param[l] || pl->param[l] == '='))) + pl = pl->next; + if (type == 't') + value.i = pl != 0; + else if (pl) { + pl->used |= 1; + opt = pl->param + l; + if (*opt == '=') + ++opt; + switch (type) { + case 'i': /* integer input */ + value.i = atoi(opt); + break; + case 'd': /* simple real input */ + value.f = pj_atof(opt); + break; + case 'r': /* degrees input */ + value.f = dmstor_ctx(ctx, opt, 0); + break; + case 's': /* char string */ value.s = (char *) opt; - break; - case 'b': /* boolean */ - switch (*opt) { - case 'F': case 'f': - value.i = 0; - break; - case '\0': case 'T': case 't': - value.i = 1; - break; - default: - pj_ctx_set_errno(ctx, -8); - value.i = 0; - break; - } - break; - default: + break; + case 'b': /* boolean */ + switch (*opt) { + case 'F': case 'f': + value.i = 0; + break; + case '\0': case 'T': case 't': + value.i = 1; + break; + default: + pj_ctx_set_errno(ctx, -8); + value.i = 0; + break; + } + break; + default: bum_type: /* note: this is an error in parameter, not a user error */ - fprintf(stderr, "invalid request to pj_param, fatal\n"); - exit(1); - } - } else /* not given */ - switch (type) { - case 'b': - case 'i': - value.i = 0; - break; - case 'd': - case 'r': - value.f = 0.; - break; - case 's': - value.s = 0; - break; - default: - goto bum_type; - } - return value; + fprintf(stderr, "invalid request to pj_param, fatal\n"); + exit(1); + } + } else /* not given */ + switch (type) { + case 'b': + case 'i': + value.i = 0; + break; + case 'd': + case 'r': + value.f = 0.; + break; + case 's': + value.s = 0; + break; + default: + goto bum_type; + } + return value; } diff --git a/src/proj.def b/src/proj.def index 887f4719..51b79558 100644 --- a/src/proj.def +++ b/src/proj.def @@ -149,3 +149,5 @@ EXPORTS pj_ellipsoid @134 pj_calc_ellipsoid_params @135 + pj_chomp @136 + pj_shrink @137 diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 5f4bf334..0fba5097 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -28,7 +28,6 @@ *****************************************************************************/ #include #include -#include #include #include "proj_internal.h" #include "projects.h" @@ -81,6 +80,8 @@ double proj_lpz_dist (const PJ *P, LPZ a, LPZ b) { PJ_COORD aa, bb; aa.lpz = a; bb.lpz = b; + if (HUGE_VAL==a.lam || HUGE_VAL==b.lam) + return HUGE_VAL; return hypot (proj_lp_dist (P, aa.lp, bb.lp), a.z - b.z); } @@ -340,7 +341,7 @@ PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { direction = PJ_INV) The conversion involves a call to the tangent function, which goes through the - roof at the poles, so very close (the last few micrometers) to the poles no + roof at the poles, so very close (the last centimeter) to the poles no conversion takes place and the input latitude is copied directly to the output. Fortunately, the geocentric latitude converges to the geographical at the @@ -349,7 +350,7 @@ PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { For the spherical case, the geographical latitude equals the geocentric, and consequently, the input is copied directly to the output. **************************************************************************************/ - const double limit = M_HALFPI - 1e-12; + const double limit = M_HALFPI - 1e-9; PJ_COORD res = coo; if ((coo.lp.phi > limit) || (coo.lp.phi < -limit) || (P->es==0)) return res; @@ -388,76 +389,29 @@ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { **************************************************************************************/ PJ *P; char *args, **argv; - int argc, i, j, last, n; + size_t argc, n; if (0==ctx) ctx = pj_get_default_ctx (); /* Make a copy that we can manipulate */ - n = (int) strlen (definition); + n = strlen (definition); args = (char *) malloc (n + 1); if (0==args) return 0; strcpy (args, definition); - /* All-in-one: count args, eliminate superfluous whitespace, 0-terminate substrings */ - for (i = j = argc = last = 0; i < n; ) { - - /* Skip prefix whitespace */ - while (isspace (args[i])) - i++; - - /* Skip at most one prefix '+' */ - if ('+'==args[i]) - i++; - - /* Whitespace after a '+' is a syntax error - but by Postel's prescription, we ignore and go on */ - if (isspace (args[i])) - continue; - - /* Move a whitespace delimited text string to the left, skipping over superfluous whitespace */ - while ((0!=args[i]) && (!isspace (args[i])) && (';'!=args[i])) - args[j++] = args[i++]; - - /* Skip postfix whitespace */ - while (isspace (args[i]) || ';'==args[i]) - i++; - - /* Greedy assignment operator: turn "a = b" into "a=b" */ - if ('='==args[i]) { - args[j++] = '='; - i++; - while (isspace (args[i])) - i++; - while ((0!=args[i]) && (!isspace (args[i])) && (';'!=args[i])) - args[j++] = args[i++]; - while (isspace (args[i]) || ';'==args[i]) - i++; - } - - /* terminate string - if that makes j pass i (often the case for first arg), let i catch up */ - args[j++] = 0; - if (i < j) - i = j; - - /* we finished another arg */ - argc++; + argc = pj_trim_argc (args); + if (argc==0) { + pj_dealloc (args); + return 0; } - /* turn the massaged input into an array of strings */ - argv = (char **) calloc (argc, sizeof (char *)); - if (0==argv) - return pj_dealloc (args); - argv[0] = args; - for (i = 0, j = 1; i < n; i++) { - if (0==args[i]) - argv[j++] = args + (i + 1); - if (j==argc) - break; - } + argv = pj_trim_argv (argc, args); /* ...and let pj_init_ctx do the hard work */ - P = pj_init_ctx (ctx, argc, argv); + P = pj_init_ctx (ctx, (int) argc, argv); + pj_dealloc (argv); pj_dealloc (args); return P; @@ -474,11 +428,22 @@ a null-pointer is returned. The definition arguments may use '+' as argument sta indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm", "zone=32"}. **************************************************************************************/ + PJ *P; + const char *c; + if (0==argv) return 0; if (0==ctx) ctx = pj_get_default_ctx (); - return pj_init_ctx (ctx, argc, argv); + + /* We assume that free format is used, and build a full proj_create compatible string */ + c = pj_make_args (argc, argv); + if (0==c) + return 0; + + P = proj_create (ctx, c); + pj_dealloc ((char *) c); + return P; } @@ -781,6 +746,8 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) { return info; } + + /*****************************************************************************/ PJ_INIT_INFO proj_init_info(const char *initname){ /****************************************************************************** @@ -790,12 +757,14 @@ PJ_INIT_INFO proj_init_info(const char *initname){ Returns PJ_INIT_INFO struct. - If the init file is not found all members of - the return struct are set to 0. If the init file is found, but it the - metadata is missing, the value is set to "Unknown". + If the init file is not found all members of the return struct are set + to the empty string. + + If the init file is found, but the metadata is missing, the value is + set to "Unknown". ******************************************************************************/ - int file_found, def_found=0; + int file_found; char param[80], key[74]; paralist *start, *next; PJ_INIT_INFO info; @@ -819,7 +788,7 @@ PJ_INIT_INFO proj_init_info(const char *initname){ strncat(param, key, 73); start = pj_mkparam(param); - next = pj_get_init(ctx, &start, start, key, &def_found); + pj_expand_init(ctx, start); if (pj_param(ctx, start, "tversion").i) { pj_strlcpy(info.version, pj_param(ctx, start, "sversion").s, sizeof(info.version)); @@ -858,6 +827,9 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0}; struct FACTORS f; + if (0==P) + return factors; + if (pj_factors(lp, P, 0.0, &f)) return factors; diff --git a/src/proj_internal.h b/src/proj_internal.h index 4e70e690..6571bce9 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -57,6 +57,7 @@ extern "C" { #define PJ_TORAD(deg) ((deg)*M_PI/180.0) #endif + /* This enum is also conditionally defined in projects.h - but we need it here */ /* for the pj_left/right prototypes, and enums cannot be forward declared */ enum pj_io_units { @@ -109,6 +110,12 @@ void pj_inherit_ellipsoid_def (const PJ *src, PJ *dst); void pj_erase_ellipsoid_def (PJ *P); int pj_calc_ellipsoid_params (PJ *P, double a, double es); +char *pj_chomp (char *c); +char *pj_shrink (char *c); +size_t pj_trim_argc (char *args); +char **pj_trim_argv (size_t argc, char *args); +char *pj_make_args (size_t argc, char **argv); + /* Lowest level: Minimum support for fileapi */ void proj_fileapi_set (PJ *P, void *fileapi); diff --git a/src/projects.h b/src/projects.h index 8c0f81fa..ca2cdbe3 100644 --- a/src/projects.h +++ b/src/projects.h @@ -683,7 +683,10 @@ double adjlon(double); double aacos(projCtx,double), aasin(projCtx,double), asqrt(double), aatan2(double, double); PROJVALUE pj_param(projCtx ctx, paralist *, const char *); +paralist *pj_param_exists (paralist *list, const char *parameter); paralist *pj_mkparam(char *); +paralist *pj_mkparam_ws (char *str); + int pj_ellipsoid (PJ *); int pj_ell_set(projCtx ctx, paralist *, double *, double *); @@ -694,7 +697,8 @@ int pj_angular_units_set(paralist *, PJ *); paralist *pj_clone_paralist( const paralist* ); paralist *pj_search_initcache( const char *filekey ); void pj_insert_initcache( const char *filekey, const paralist *list); -paralist *pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def); +paralist *pj_expand_init(projCtx ctx, paralist *init); + void *pj_dealloc_params (projCtx ctx, paralist *start, int errlev); -- cgit v1.2.3 From 6cd160fe5c96559a7c388647244f8cfa16426b3d Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Sun, 17 Dec 2017 17:22:00 +0100 Subject: Normalize whitespace in multistresstest.c --- src/multistresstest.c | 104 +++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/multistresstest.c b/src/multistresstest.c index ddbbb033..c9c11c6d 100644 --- a/src/multistresstest.c +++ b/src/multistresstest.c @@ -32,10 +32,10 @@ #include "proj_api.h" #ifdef _WIN32 - #include + #include #else - #include - #include + #include + #include #endif #define num_threads 10 @@ -49,10 +49,10 @@ typedef struct { double src_x, src_y, src_z; double dst_x, dst_y, dst_z; - + int dst_error; int skip; -} TestItem; +} TestItem; TestItem test_list[] = { { @@ -192,7 +192,7 @@ static projPJ* custom_pj_init_plus_ctx(projCtx ctx, const char* def) static void TestThread() { - int i, test_count = sizeof(test_list) / sizeof(TestItem); + int i, test_count = sizeof(test_list) / sizeof(TestItem); int repeat_count = num_iterations; int i_iter; @@ -202,10 +202,10 @@ static void TestThread() projPJ *src_pj_list, *dst_pj_list; projCtx ctx = pj_ctx_alloc(); // projCtx ctx = pj_get_default_ctx(); - + src_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); dst_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); - + if(!reinit_every_iteration) { for( i = 0; i < test_count; i++ ) @@ -216,11 +216,11 @@ static void TestThread() dst_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->dst_def ); } } - + /* -------------------------------------------------------------------- */ /* Perform tests - over and over. */ /* -------------------------------------------------------------------- */ - + for( i_iter = 0; i_iter < repeat_count; i_iter++ ) { for( i = 0; i < test_count; i++ ) @@ -240,7 +240,7 @@ static void TestThread() { int skipTest = (src_pj_list[i] == NULL || dst_pj_list[i] == NULL); - + if ( skipTest != test->skip ) fprintf( stderr, "Threaded projection initialization does not match unthreaded initialization\n" ); @@ -256,23 +256,23 @@ static void TestThread() if ( test->skip ) continue; - error = pj_transform( src_pj_list[i], dst_pj_list[i], 1, 0, + error = pj_transform( src_pj_list[i], dst_pj_list[i], 1, 0, &x, &y, &z ); - + if( error != test->dst_error ) { - fprintf( stderr, "Got error %d, expected %d\n", + fprintf( stderr, "Got error %d, expected %d\n", error, test->dst_error ); } if( x != test->dst_x || y != test->dst_y || z != test->dst_z ) { - fprintf( stderr, + fprintf( stderr, "Got %.15g,%.15g,%.15g\n" "Expected %.15g,%.15g,%.15g\n" "Diff %.15g,%.15g,%.15g\n", - x, y, z, + x, y, z, test->dst_x, test->dst_y, test->dst_z, x-test->dst_x, y-test->dst_y, z-test->dst_z); } @@ -296,13 +296,13 @@ static void TestThread() pj_free( dst_pj_list[i] ); } } - + free( src_pj_list ); free( dst_pj_list ); pj_ctx_free( ctx ); - printf( "%d iterations of the %d tests complete in thread X\n", + printf( "%d iterations of the %d tests complete in thread X\n", repeat_count, test_count ); active_thread_count--; @@ -348,7 +348,7 @@ static int do_main(void) /* Our first pass is to establish the correct answers for all */ /* the tests. */ /* -------------------------------------------------------------------- */ - int i, test_count = sizeof(test_list) / sizeof(TestItem); + int i, test_count = sizeof(test_list) / sizeof(TestItem); for( i = 0; i < test_count; i++ ) { @@ -374,16 +374,16 @@ static int do_main(void) pj_free (src_pj); continue; } - + test->dst_x = test->src_x; test->dst_y = test->src_y; test->dst_z = test->src_z; - test->dst_error = pj_transform( src_pj, dst_pj, 1, 0, - &(test->dst_x), + test->dst_error = pj_transform( src_pj, dst_pj, 1, 0, + &(test->dst_x), &(test->dst_y), &(test->dst_z) ); - + pj_free( src_pj ); pj_free( dst_pj ); @@ -401,47 +401,47 @@ static int do_main(void) /* -------------------------------------------------------------------- */ #ifdef _WIN32 - { //Scoped to workaround lack of c99 support in VS - HANDLE ahThread[num_threads]; + { //Scoped to workaround lack of c99 support in VS + HANDLE ahThread[num_threads]; - for( i = 0; i < num_threads; i++ ) - { - active_thread_count++; + for( i = 0; i < num_threads; i++ ) + { + active_thread_count++; - ahThread[i] = CreateThread(NULL, 0, WinTestThread, NULL, 0, NULL); - - if (ahThread[i] == 0) - { - printf( "Thread creation failed."); - return 1; - } - } + ahThread[i] = CreateThread(NULL, 0, WinTestThread, NULL, 0, NULL); - printf( "%d test threads launched.\n", num_threads ); + if (ahThread[i] == 0) + { + printf( "Thread creation failed."); + return 1; + } + } - WaitForMultipleObjects(num_threads, ahThread, TRUE, INFINITE); - } + printf( "%d test threads launched.\n", num_threads ); + + WaitForMultipleObjects(num_threads, ahThread, TRUE, INFINITE); + } #else { - pthread_t ahThread[num_threads]; - pthread_attr_t hThreadAttr; + pthread_t ahThread[num_threads]; + pthread_attr_t hThreadAttr; - pthread_attr_init( &hThreadAttr ); - pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); + pthread_attr_init( &hThreadAttr ); + pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); - for( i = 0; i < num_threads; i++ ) - { - active_thread_count++; + for( i = 0; i < num_threads; i++ ) + { + active_thread_count++; - pthread_create( &(ahThread[i]), &hThreadAttr, - PosixTestThread, NULL ); - } + pthread_create( &(ahThread[i]), &hThreadAttr, + PosixTestThread, NULL ); + } - printf( "%d test threads launched.\n", num_threads ); + printf( "%d test threads launched.\n", num_threads ); - while( active_thread_count > 0 ) - sleep( 1 ); + while( active_thread_count > 0 ) + sleep( 1 ); } #endif -- cgit v1.2.3 From 80a5172acd1d75b35727132a95151c9ecbbe6899 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Sun, 17 Dec 2017 15:54:39 +0100 Subject: Add -std=c89 to travis targets. The multistresstest code has been made C89 compliant in the process. --- src/multistresstest.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/multistresstest.c b/src/multistresstest.c index c9c11c6d..8e7517ec 100644 --- a/src/multistresstest.c +++ b/src/multistresstest.c @@ -161,7 +161,7 @@ TestItem test_list[] = { 0, 0 }, { - //Bad projection (invalid ellipsoid parameter +R_A=0) + /* Bad projection (invalid ellipsoid parameter +R_A=0) */ "+proj=utm +zone=11 +datum=WGS84", "+proj=merc +datum=potsdam +R_A=0", 150000.0, 3000000.0, 0.0, @@ -201,7 +201,6 @@ static void TestThread() /* -------------------------------------------------------------------- */ projPJ *src_pj_list, *dst_pj_list; projCtx ctx = pj_ctx_alloc(); -// projCtx ctx = pj_get_default_ctx(); src_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); dst_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); -- cgit v1.2.3 From a07501a165e6f2521c9aa13fa63fab33cf67d876 Mon Sep 17 00:00:00 2001 From: Aaron Puchert Date: Fri, 17 Nov 2017 17:46:34 +0100 Subject: Declare non-local variables as const where possible Having non-const variables of static lifetime or even global scope is usually a bad idea. These variables are inherently constants, and this should be enforced. This required marking some functions as not modifying input parameters and marking some pointers as pointers to const. One advantage is that the compiler usually puts const static variables in a read-only code segment, so they can't be modified physically. This can be verified with `nm` (on POSIX systems). To avoid changes to the public API, functions returning non-const pointers to data tables were left intact, but the returned data may not be modified. Internally we prefer using the proj_list_* functions over the pj_get_*_ref functions, because the former return const pointers. --- src/PJ_healpix.c | 10 +++++----- src/PJ_isea.c | 4 ++-- src/PJ_mod_ster.c | 16 ++++++++-------- src/PJ_nzmg.c | 17 +++++++++-------- src/PJ_pipeline.c | 4 ++-- src/PJ_unitconvert.c | 2 +- src/cs2cs.c | 23 ++++++++++++----------- src/emess.c | 2 +- src/emess.h | 2 +- src/geod.c | 10 ++++++---- src/geod_set.c | 3 ++- src/geodtest.c | 6 +++--- src/gie.c | 6 ++---- src/multistresstest.c | 2 +- src/nad2bin.c | 4 ++-- src/nad_init.c | 4 ++-- src/pj_datums.c | 15 +++++++++------ src/pj_ell_set.c | 6 +++--- src/pj_ellps.c | 9 ++++++++- src/pj_gauss.c | 4 ++-- src/pj_gc_reader.c | 2 +- src/pj_gridinfo.c | 4 ++-- src/pj_init.c | 2 +- src/pj_list.c | 11 +++++++++-- src/pj_open_lib.c | 6 +++--- src/pj_strerrno.c | 4 ++-- src/pj_units.c | 10 +++++++++- src/pj_zpoly1.c | 4 ++-- src/proj.c | 22 ++++++++++++---------- src/proj_4D_api.c | 19 +------------------ src/proj_internal.h | 2 +- src/proj_mdist.c | 4 ++-- src/projects.h | 4 ++-- src/test228.c | 4 ++-- 34 files changed, 130 insertions(+), 117 deletions(-) (limited to 'src') diff --git a/src/PJ_healpix.c b/src/PJ_healpix.c index 41f62ee4..2d62050c 100644 --- a/src/PJ_healpix.c +++ b/src/PJ_healpix.c @@ -64,7 +64,7 @@ typedef struct { enum Region {north, south, equatorial} region; } CapMap; -double rot[7][2][2] = ROT; +static const double rot[7][2][2] = ROT; /** * Returns the sign of the double. @@ -310,7 +310,7 @@ static LP healpix_sphere_inverse(XY xy) { * Return the vector sum a + b, where a and b are 2-dimensional vectors. * @param ret holds a + b. **/ -static void vector_add(double a[2], double b[2], double *ret) { +static void vector_add(const double a[2], const double b[2], double *ret) { int i; for(i = 0; i < 2; i++) { ret[i] = a[i] + b[i]; @@ -322,7 +322,7 @@ static void vector_add(double a[2], double b[2], double *ret) { * Return the vector difference a - b, where a and b are 2-dimensional vectors. * @param ret holds a - b. **/ -static void vector_sub(double a[2], double b[2], double*ret) { +static void vector_sub(const double a[2], const double b[2], double*ret) { int i; for(i = 0; i < 2; i++) { ret[i] = a[i] - b[i]; @@ -335,7 +335,7 @@ static void vector_sub(double a[2], double b[2], double*ret) { * b is a 2 x 1 matrix. * @param ret holds a*b. **/ -static void dot_product(double a[2][2], double b[2], double *ret) { +static void dot_product(const double a[2][2], const double b[2], double *ret) { int i, j; int length = 2; for(i = 0; i < length; i++) { @@ -453,7 +453,7 @@ static XY combine_caps(double x, double y, int north_square, int south_square, double vector[2]; double v_min_c[2]; double ret_dot[2]; - double (*tmpRot)[2]; + const double (*tmpRot)[2]; int pole = 0; CapMap capmap = get_cap(x, y, north_square, south_square, inverse); diff --git a/src/PJ_isea.c b/src/PJ_isea.c index bf006a62..9baea8b5 100644 --- a/src/PJ_isea.c +++ b/src/PJ_isea.c @@ -159,7 +159,7 @@ struct snyder_constants { }; /* TODO put these in radians to avoid a later conversion */ -ISEA_STATIC +ISEA_STATIC const struct snyder_constants constants[] = { {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0}, {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027}, @@ -217,7 +217,7 @@ static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, #define F_RAD 0.18871053072122403508 /* triangle Centers */ -struct isea_geo icostriangles[] = { +static const struct isea_geo icostriangles[] = { {0.0, 0.0}, {-DEG144, E_RAD}, {-DEG72, E_RAD}, diff --git a/src/PJ_mod_ster.c b/src/PJ_mod_ster.c index c8e4c6b8..d807660c 100644 --- a/src/PJ_mod_ster.c +++ b/src/PJ_mod_ster.c @@ -12,7 +12,7 @@ PROJ_HEAD(gs50, "Mod. Stereographic of 50 U.S.") "\n\tAzi(mod)"; #define EPSLN 1e-12 struct pj_opaque { - COMPLEX *zcoeff; \ + const COMPLEX *zcoeff; \ double cchio, schio; \ int n; }; @@ -120,7 +120,7 @@ static PJ *setup(PJ *P) { /* general initialization */ /* Miller Oblated Stereographic */ PJ *PROJECTION(mil_os) { - static COMPLEX AB[] = { + static const COMPLEX AB[] = { {0.924500, 0.}, {0., 0.}, {0.019430, 0.} @@ -143,7 +143,7 @@ PJ *PROJECTION(mil_os) { /* Lee Oblated Stereographic */ PJ *PROJECTION(lee_os) { - static COMPLEX AB[] = { + static const COMPLEX AB[] = { {0.721316, 0.}, {0., 0.}, {-0.0088162, -0.00617325} @@ -165,7 +165,7 @@ PJ *PROJECTION(lee_os) { PJ *PROJECTION(gs48) { - static COMPLEX /* 48 United States */ + static const COMPLEX /* 48 United States */ AB[] = { {0.98879, 0.}, {0., 0.}, @@ -191,7 +191,7 @@ PJ *PROJECTION(gs48) { PJ *PROJECTION(alsk) { - static COMPLEX ABe[] = { /* Alaska ellipsoid */ + static const COMPLEX ABe[] = { /* Alaska ellipsoid */ { .9945303, 0.}, { .0052083, -.0027404}, { .0072721, .0048181}, @@ -200,7 +200,7 @@ PJ *PROJECTION(alsk) { { .3582802, -.2884586}, }; - static COMPLEX ABs[] = { /* Alaska sphere */ + static const COMPLEX ABs[] = { /* Alaska sphere */ { .9972523, 0.}, { .0052513, -.0041175}, { .0074606, .0048125}, @@ -231,7 +231,7 @@ PJ *PROJECTION(alsk) { PJ *PROJECTION(gs50) { - static COMPLEX ABe[] = { /* GS50 ellipsoid */ + static const COMPLEX ABe[] = { /* GS50 ellipsoid */ { .9827497, 0.}, { .0210669, .0053804}, {-.1031415, -.0571664}, @@ -244,7 +244,7 @@ PJ *PROJECTION(gs50) { {-.0210072, .0834037} }; - static COMPLEX ABs[] = { /* GS50 sphere */ + static const COMPLEX ABs[] = { /* GS50 sphere */ { .9842990, 0.}, { .0211642, .0037608}, {-.1036018, -.0575102}, diff --git a/src/PJ_nzmg.c b/src/PJ_nzmg.c index 2d65a6e1..b30deabf 100644 --- a/src/PJ_nzmg.c +++ b/src/PJ_nzmg.c @@ -34,7 +34,7 @@ PROJ_HEAD(nzmg, "New Zealand Map Grid") "\n\tfixed Earth"; #define SEC5_TO_RAD 0.4848136811095359935899141023 #define RAD_TO_SEC5 2.062648062470963551564733573 -static COMPLEX bf[] = { +static const COMPLEX bf[] = { { .7557853228, 0.0}, { .249204646, 0.003371507}, {-.001541739, 0.041058560}, @@ -42,12 +42,12 @@ static COMPLEX bf[] = { {-.26623489, -0.36249218}, {-.6870983, -1.1651967} }; -static double tphi[] = { 1.5627014243, .5185406398, -.03333098, - -.1052906, -.0368594, .007317, - .01220, .00394, -.0013 }; +static const double tphi[] = { 1.5627014243, .5185406398, -.03333098, + -.1052906, -.0368594, .007317, + .01220, .00394, -.0013 }; -static double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879, - -.0055161, .0026906, -.001333, .00067, -.00034 }; +static const double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879, + -.0055161, .0026906, -.001333, .00067, -.00034 }; #define Nbf 5 #define Ntpsi 9 @@ -57,7 +57,7 @@ static double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .011 static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ XY xy = {0.0,0.0}; COMPLEX p; - double *C; + const double *C; int i; lp.phi = (lp.phi - P->phi0) * RAD_TO_SEC5; @@ -77,7 +77,8 @@ static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ LP lp = {0.0,0.0}; int nn, i; COMPLEX p, f, fp, dp; - double den, *C; + double den; + const double *C; p.r = xy.y; p.i = xy.x; diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index a0ef7c1e..2b012193 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -273,7 +273,7 @@ static size_t argc_params (paralist *params) { } /* Sentinel for argument list */ -static char argv_sentinel[] = "step"; +static char *argv_sentinel = "step"; /* turn paralist into argc/argv style argument list */ static char **argv_params (paralist *params, size_t argc) { @@ -363,7 +363,7 @@ PJ *OPERATION(pipeline,0) { /* Do some syntactical sanity checking */ for (i = 0; i < argc; i++) { - if (0==strcmp ("step", argv[i])) { + if (0==strcmp (argv_sentinel, argv[i])) { if (-1==i_pipeline) { proj_log_error (P, "Pipeline: +step before +proj=pipeline"); return destructor (P, PJD_ERR_MALFORMED_PIPELINE); diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c index fc90821b..106caabf 100644 --- a/src/PJ_unitconvert.c +++ b/src/PJ_unitconvert.c @@ -256,7 +256,7 @@ static double mjd_to_yyyymmdd(double mjd) { } -struct TIME_UNITS time_units[] = { +static const struct TIME_UNITS time_units[] = { {"mjd", mjd_to_mjd, mjd_to_mjd, "Modified julian date"}, {"decimalyear", decimalyear_to_mjd, mjd_to_decimalyear, "Decimal year"}, {"gps_week", gps_week_to_mjd, mjd_to_gps_week, "GPS Week"}, diff --git a/src/cs2cs.c b/src/cs2cs.c index 7afeea73..730d37ee 100644 --- a/src/cs2cs.c +++ b/src/cs2cs.c @@ -26,6 +26,7 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ +#include "proj.h" #include "projects.h" #include #include @@ -206,11 +207,11 @@ int main(int argc, char **argv) case 'l': /* list projections, ellipses or units */ if (!arg[1] || arg[1] == 'p' || arg[1] == 'P') { /* list projections */ - struct PJ_LIST *lp; + const struct PJ_LIST *lp; int do_long = arg[1] == 'P', c; char *str; - for (lp = pj_get_list_ref() ; lp->id ; ++lp) { + for (lp = proj_list_operations() ; lp->id ; ++lp) { (void)printf("%s : ", lp->id); if (do_long) /* possibly multiline description */ (void)puts(*lp->descr); @@ -222,28 +223,28 @@ int main(int argc, char **argv) } } } else if (arg[1] == '=') { /* list projection 'descr' */ - struct PJ_LIST *lp; + const struct PJ_LIST *lp; arg += 2; - for (lp = pj_get_list_ref() ; lp->id ; ++lp) + for (lp = proj_list_operations() ; 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; + const struct PJ_ELLPS *le; - for (le = pj_get_ellps_ref(); le->id ; ++le) + for (le = proj_list_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; + const struct PJ_UNITS *lu; - for (lu = pj_get_units_ref(); lu->id ; ++lu) + for (lu = proj_list_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; + const struct PJ_DATUMS *ld; printf("__datum_id__ __ellipse___ __definition/comments______________________________\n" ); for (ld = pj_get_datums_ref(); ld->id ; ++ld) @@ -254,9 +255,9 @@ int main(int argc, char **argv) printf( "%25s %s\n", " ", ld->comments ); } } else if( arg[1] == 'm') { /* list prime meridians */ - struct PJ_PRIME_MERIDIANS *lpm; + const struct PJ_PRIME_MERIDIANS *lpm; - for (lpm = pj_get_prime_meridians_ref(); lpm->id ; ++lpm) + for (lpm = proj_list_prime_meridians(); lpm->id ; ++lpm) (void)printf("%12s %-30s\n", lpm->id, lpm->defn); } else diff --git a/src/emess.c b/src/emess.c index 7b6ebf75..dc4cb824 100644 --- a/src/emess.c +++ b/src/emess.c @@ -18,7 +18,7 @@ #define EMESS_ROUTINE #include "emess.h" void -emess(int code, char *fmt, ...) { +emess(int code, const char *fmt, ...) { va_list args; va_start(args, fmt); diff --git a/src/emess.h b/src/emess.h index b694d61e..4a6f7587 100644 --- a/src/emess.h +++ b/src/emess.h @@ -24,6 +24,6 @@ extern struct EMESS emess_dat; #endif /* use type */ -void emess(int, char *, ...); +void emess(int, const char *, ...); #endif /* end EMESS_H */ diff --git a/src/geod.c b/src/geod.c index 755afbd7..e90d71b5 100644 --- a/src/geod.c +++ b/src/geod.c @@ -1,4 +1,6 @@ /* <<<< Geodesic filter program >>>> */ + +#include "proj.h" # include "projects.h" # include "geod_interface.h" # include "emess.h" @@ -175,15 +177,15 @@ noargument: emess(1,"missing argument for -%c",*arg); continue; case 'l': if (!arg[1] || arg[1] == 'e') { /* list of ellipsoids */ - struct PJ_ELLPS *le; + const struct PJ_ELLPS *le; - for (le=pj_get_ellps_ref(); le->id ; ++le) + for (le=proj_list_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 of units */ - struct PJ_UNITS *lu; + const struct PJ_UNITS *lu; - for (lu = pj_get_units_ref();lu->id ; ++lu) + for (lu = proj_list_units();lu->id ; ++lu) (void)printf("%12s %-20s %s\n", lu->id, lu->to_meter, lu->name); } else diff --git a/src/geod_set.c b/src/geod_set.c index 16396a90..26a86b61 100644 --- a/src/geod_set.c +++ b/src/geod_set.c @@ -2,6 +2,7 @@ #define _IN_GEOD_SET #include +#include "proj.h" #include "projects.h" #include "geod_interface.h" #include "emess.h" @@ -29,7 +30,7 @@ geod_set(int argc, char **argv) { /* set units */ if ((name = pj_param(NULL,start, "sunits").s) != NULL) { char *s; - struct PJ_UNITS *unit_list = pj_get_units_ref(); + const struct PJ_UNITS *unit_list = proj_list_units(); for (i = 0; (s = unit_list[i].id) && strcmp(name, s) ; ++i) ; if (!s) emess(1,"%s unknown unit conversion id", name); diff --git a/src/geodtest.c b/src/geodtest.c index 6899436c..5b41b72c 100644 --- a/src/geodtest.c +++ b/src/geodtest.c @@ -20,7 +20,7 @@ # pragma warning (disable: 4706) #endif -double wgs84_a = 6378137, wgs84_f = 1/298.257223563; /* WGS84 */ +static const double wgs84_a = 6378137, wgs84_f = 1/298.257223563; /* WGS84 */ static int assertEquals(double x, double y, double d) { if (fabs(x - y) <= d) @@ -29,8 +29,8 @@ static int assertEquals(double x, double y, double d) { return 1; } -const int ncases = 20; -double testcases[20][12] = { +static const int ncases = 20; +static const double testcases[20][12] = { {35.60777, -139.44815, 111.098748429560326, -11.17491, -69.95921, 129.289270889708762, 8935244.5604818305, 80.50729714281974, 6273170.2055303837, diff --git a/src/gie.c b/src/gie.c index e23ca92b..2ae2ddee 100644 --- a/src/gie.c +++ b/src/gie.c @@ -199,8 +199,6 @@ static const char delim[] = {"-------------------------------------------------- #define CMDLEN 250000 -int nfiles = 0; - static const char usage[] = { "--------------------------------------------------------------------------------\n" @@ -1316,7 +1314,7 @@ whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwis -char tc32_utm32[] = { +static const char tc32_utm32[] = { " +proj=horner" " +ellps=intl" " +range=500000" @@ -1330,7 +1328,7 @@ char tc32_utm32[] = { }; -char sb_utm32[] = { +static const char sb_utm32[] = { " +proj=horner" " +ellps=intl" " +range=500000" diff --git a/src/multistresstest.c b/src/multistresstest.c index 8e7517ec..e32c7ae4 100644 --- a/src/multistresstest.c +++ b/src/multistresstest.c @@ -54,7 +54,7 @@ typedef struct { int skip; } TestItem; -TestItem test_list[] = { +static TestItem test_list[] = { { "+proj=utm +zone=11 +datum=WGS84", "+proj=latlong +datum=WGS84", diff --git a/src/nad2bin.c b/src/nad2bin.c index 8401df79..03a55326 100644 --- a/src/nad2bin.c +++ b/src/nad2bin.c @@ -13,8 +13,8 @@ /* Convert the byte order of the given word(s) in place. */ /************************************************************************/ -static int byte_order_test = 1; -#define IS_LSB (((unsigned char *) (&byte_order_test))[0] == 1) +static const int byte_order_test = 1; +#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) static void swap_words( void *data_in, int word_size, int word_count ) diff --git a/src/nad_init.c b/src/nad_init.c index 99342aa5..04177a44 100644 --- a/src/nad_init.c +++ b/src/nad_init.c @@ -38,8 +38,8 @@ /* Convert the byte order of the given word(s) in place. */ /************************************************************************/ -static int byte_order_test = 1; -#define IS_LSB (((unsigned char *) (&byte_order_test))[0] == 1) +static const int byte_order_test = 1; +#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) static void swap_words( void *data_in, int word_size, int word_count ) diff --git a/src/pj_datums.c b/src/pj_datums.c index c9655cd6..834ce724 100644 --- a/src/pj_datums.c +++ b/src/pj_datums.c @@ -25,8 +25,9 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PJ_DATUMS__ +#include "proj.h" +#define PJ_DATUMS__ #include /* @@ -35,7 +36,7 @@ * datum name for the comments if available. */ -C_NAMESPACE_VAR struct PJ_DATUMS pj_datums[] = { +C_NAMESPACE_VAR const struct PJ_DATUMS pj_datums[] = { /* id definition ellipse comments */ /* -- ---------- ------- -------- */ {"WGS84", "towgs84=0,0,0", "WGS84", ""}, @@ -65,12 +66,11 @@ C_NAMESPACE_VAR struct PJ_DATUMS pj_datums[] = { }; struct PJ_DATUMS *pj_get_datums_ref() - { - return pj_datums; + return (struct PJ_DATUMS *)pj_datums; } -C_NAMESPACE_VAR struct PJ_PRIME_MERIDIANS pj_prime_meridians[] = { +C_NAMESPACE_VAR const struct PJ_PRIME_MERIDIANS pj_prime_meridians[] = { /* id definition */ /* -- ---------- */ {"greenwich", "0dE"}, @@ -90,8 +90,11 @@ C_NAMESPACE_VAR struct PJ_PRIME_MERIDIANS pj_prime_meridians[] = { }; struct PJ_PRIME_MERIDIANS *pj_get_prime_meridians_ref() +{ + return (struct PJ_PRIME_MERIDIANS *)pj_prime_meridians; +} +const PJ_PRIME_MERIDIANS *proj_list_prime_meridians(void) { return pj_prime_meridians; } - diff --git a/src/pj_ell_set.c b/src/pj_ell_set.c index d21122e3..ab2fa818 100644 --- a/src/pj_ell_set.c +++ b/src/pj_ell_set.c @@ -14,7 +14,7 @@ static int ellps_spherification (PJ *P); static paralist *pj_get_param (paralist *list, char *key); static char *pj_param_value (paralist *list); -static PJ_ELLPS *pj_find_ellps (char *name); +static const PJ_ELLPS *pj_find_ellps (char *name); /***************************************************************************************/ @@ -125,7 +125,7 @@ int pj_ellipsoid (PJ *P) { static int ellps_ellps (PJ *P) { /***************************************************************************************/ PJ B; - PJ_ELLPS *ellps; + const PJ_ELLPS *ellps; paralist *par = 0; char *name; int err; @@ -419,7 +419,7 @@ static char *pj_param_value (paralist *list) { } -static PJ_ELLPS *pj_find_ellps (char *name) { +static const PJ_ELLPS *pj_find_ellps (char *name) { int i; char *s; if (0==name) diff --git a/src/pj_ellps.c b/src/pj_ellps.c index 2a2c5d3b..af5990dd 100644 --- a/src/pj_ellps.c +++ b/src/pj_ellps.c @@ -1,8 +1,11 @@ /* definition of standard geoids */ + +#include "proj.h" + #define PJ_ELLPS__ #include "projects.h" -C_NAMESPACE_VAR struct PJ_ELLPS +C_NAMESPACE_VAR const struct PJ_ELLPS pj_ellps[] = { {"MERIT", "a=6378137.0", "rf=298.257", "MERIT 1983"}, {"SGS85", "a=6378136.0", "rf=298.257", "Soviet Geodetic System 85"}, @@ -53,7 +56,11 @@ pj_ellps[] = { }; struct PJ_ELLPS *pj_get_ellps_ref() +{ + return (struct PJ_ELLPS *)pj_ellps; +} +const PJ_ELLPS *proj_list_ellps(void) { return pj_ellps; } diff --git a/src/pj_gauss.c b/src/pj_gauss.c index 41ee8abf..bd49b6c2 100644 --- a/src/pj_gauss.c +++ b/src/pj_gauss.c @@ -65,7 +65,7 @@ void *pj_gauss_ini(double e, double phi0, double *chi, double *rc) { } LP pj_gauss(projCtx ctx, LP elp, const void *data) { - struct GAUSS *en = (struct GAUSS *)data; + const struct GAUSS *en = (const struct GAUSS *)data; LP slp; (void) ctx; @@ -77,7 +77,7 @@ LP pj_gauss(projCtx ctx, LP elp, const void *data) { } LP pj_inv_gauss(projCtx ctx, LP slp, const void *data) { - struct GAUSS *en = (struct GAUSS *)data; + const struct GAUSS *en = (const struct GAUSS *)data; LP elp; double num; int i; diff --git a/src/pj_gc_reader.c b/src/pj_gc_reader.c index e49e56a4..90610a17 100644 --- a/src/pj_gc_reader.c +++ b/src/pj_gc_reader.c @@ -47,7 +47,7 @@ PJ_GridCatalog *pj_gc_readcatalog( projCtx ctx, const char *catalog_name ) int entry_max; char line[302]; - fid = pj_open_lib( ctx, (char *) catalog_name, "r" ); + fid = pj_open_lib( ctx, catalog_name, "r" ); if (fid == NULL) return NULL; diff --git a/src/pj_gridinfo.c b/src/pj_gridinfo.c index 9b9a8d82..b1b39e01 100644 --- a/src/pj_gridinfo.c +++ b/src/pj_gridinfo.c @@ -40,8 +40,8 @@ /* Convert the byte order of the given word(s) in place. */ /************************************************************************/ -static int byte_order_test = 1; -#define IS_LSB (1 == ((unsigned char *) (&byte_order_test))[0]) +static const int byte_order_test = 1; +#define IS_LSB (1 == ((const unsigned char *) (&byte_order_test))[0]) static void swap_words( unsigned char *data, int word_size, int word_count ) diff --git a/src/pj_init.c b/src/pj_init.c index 8f4eb477..7e885575 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -639,7 +639,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { /* Axis orientation */ if( (pj_param(ctx, start,"saxis").s) != NULL ) { - static const char *axis_legal = "ewnsud"; + 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); diff --git a/src/pj_list.c b/src/pj_list.c index 2bdd3053..d34e055d 100644 --- a/src/pj_list.c +++ b/src/pj_list.c @@ -2,6 +2,8 @@ ** Use local definition of PJ_LIST_H for subset. */ +#include "proj.h" + #define USE_PJ_LIST_H 1 #include "projects.h" @@ -18,13 +20,18 @@ /* Generate the null-terminated list of projection functions with associated mnemonics and descriptions */ #define PROJ_HEAD(id, name) {#id, pj_##id, &pj_s_##id}, -struct PJ_LIST pj_list[] = { +const struct PJ_LIST pj_list[] = { #include "pj_list.h" {0, 0, 0}, }; #undef PROJ_HEAD -struct PJ_LIST *pj_get_list_ref (void) { +struct PJ_LIST *pj_get_list_ref() +{ + return (struct PJ_LIST *)pj_list; +} + +const PJ_OPERATIONS *proj_list_operations(void) { return pj_list; } diff --git a/src/pj_open_lib.c b/src/pj_open_lib.c index 08532beb..4eaefba1 100644 --- a/src/pj_open_lib.c +++ b/src/pj_open_lib.c @@ -38,7 +38,7 @@ static const char *(*pj_finder)(const char *) = NULL; static int path_count = 0; static char **search_path = NULL; -static char * proj_lib_name = +static const char * proj_lib_name = #ifdef PROJ_LIB PROJ_LIB; #else @@ -93,8 +93,8 @@ void pj_set_searchpath ( int count, const char **path ) /* just a couple of helper functions that lets other functions access the otherwise private search path */ -const char **proj_get_searchpath(void) { - return (const char **)search_path; +const char * const *proj_get_searchpath(void) { + return (const char * const *)search_path; } int proj_get_path_count(void) { diff --git a/src/pj_strerrno.c b/src/pj_strerrno.c index 3e726895..c2221e58 100644 --- a/src/pj_strerrno.c +++ b/src/pj_strerrno.c @@ -4,7 +4,7 @@ #include #include - static char * + static const char * const pj_err_list[] = { "no arguments in initialization list", /* -1 */ "no options found in 'init' file", /* -2 */ @@ -91,7 +91,7 @@ char *pj_strerrno(int err) { /* PROJ.4 error codes are negative */ adjusted_err = - err - 1; if (adjusted_err < (sizeof(pj_err_list) / sizeof(char *))) - return(pj_err_list[adjusted_err]); + return (char *)pj_err_list[adjusted_err]; sprintf( note, "invalid projection system error (%d)", (err > -9999)? err: -9999); return note; diff --git a/src/pj_units.c b/src/pj_units.c index 7dbae34c..fe4ad453 100644 --- a/src/pj_units.c +++ b/src/pj_units.c @@ -1,10 +1,14 @@ /* definition of standard cartesian units */ + +#include "proj.h" + #define PJ_UNITS__ #include + /* Field 2 that contains the multiplier to convert named units to meters ** may be expressed by either a simple floating point constant or a ** numerator/denomenator values (e.g. 1/1000) */ -C_NAMESPACE_VAR struct PJ_UNITS +C_NAMESPACE_VAR const struct PJ_UNITS pj_units[] = { {"km", "1000.", "Kilometer", 1000.0}, {"m", "1.", "Meter", 1.0}, @@ -31,7 +35,11 @@ pj_units[] = { }; struct PJ_UNITS *pj_get_units_ref() +{ + return (struct PJ_UNITS *)pj_units; +} +const PJ_UNITS *proj_list_units() { return pj_units; } diff --git a/src/pj_zpoly1.c b/src/pj_zpoly1.c index 3d6418bb..626a1fed 100644 --- a/src/pj_zpoly1.c +++ b/src/pj_zpoly1.c @@ -5,7 +5,7 @@ ** n should always be >= 1 though no checks are made */ COMPLEX -pj_zpoly1(COMPLEX z, COMPLEX *C, int n) { +pj_zpoly1(COMPLEX z, const COMPLEX *C, int n) { COMPLEX a; double t; @@ -20,7 +20,7 @@ pj_zpoly1(COMPLEX z, COMPLEX *C, int n) { } /* evaluate complex polynomial and derivative */ COMPLEX -pj_zpolyd1(COMPLEX z, COMPLEX *C, int n, COMPLEX *der) { +pj_zpolyd1(COMPLEX z, const COMPLEX *C, int n, COMPLEX *der) { COMPLEX a, b; double t; int first = 1; diff --git a/src/proj.c b/src/proj.c index 7922a5ac..1d702ea4 100644 --- a/src/proj.c +++ b/src/proj.c @@ -45,7 +45,9 @@ static int static char *cheby_str, /* string controlling Chebychev evaluation */ - *oform = (char *)0, /* output format for x-y or decimal degrees */ + *oform = (char *)0; /* output format for x-y or decimal degrees */ + +static const char *oterr = "*\t*", /* output line for unprojectable input */ *usage = "%s\nusage: %s [ -beEfiIlormsStTvVwW [args] ] [ +opts[=arg] ] [ files ]\n"; @@ -356,11 +358,11 @@ int main(int argc, char **argv) { case 'l': /* list projections, ellipses or units */ if (!arg[1] || arg[1] == 'p' || arg[1] == 'P') { /* list projections */ - struct PJ_LIST *lp; + const struct PJ_LIST *lp; int do_long = arg[1] == 'P', c; char *str; - for (lp = pj_get_list_ref() ; lp->id ; ++lp) { + for (lp = proj_list_operations() ; lp->id ; ++lp) { if( strcmp(lp->id,"latlong") == 0 || strcmp(lp->id,"longlat") == 0 || strcmp(lp->id,"geocent") == 0 ) @@ -377,28 +379,28 @@ int main(int argc, char **argv) { } } } else if (arg[1] == '=') { /* list projection 'descr' */ - struct PJ_LIST *lp; + const struct PJ_LIST *lp; arg += 2; - for (lp = pj_get_list_ref(); lp->id ; ++lp) + for (lp = proj_list_operations(); 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; + const struct PJ_ELLPS *le; - for (le = pj_get_ellps_ref(); le->id ; ++le) + for (le = proj_list_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; + const struct PJ_UNITS *lu; - for (lu = pj_get_units_ref(); lu->id ; ++lu) + for (lu = proj_list_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; + const struct PJ_DATUMS *ld; printf("__datum_id__ __ellipse___ __definition/comments______________________________\n" ); for (ld = pj_get_datums_ref(); ld->id ; ++ld) diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 0fba5097..a0550727 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -597,7 +597,7 @@ PJ_INFO proj_info(void) { ******************************************************************************/ PJ_INFO info; - const char **paths; + const char * const *paths; char *tmpstr; int i, n; size_t len = 0; @@ -847,20 +847,3 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { return factors; } - -const PJ_ELLPS *proj_list_ellps(void) { - return pj_get_ellps_ref(); -} - -const PJ_UNITS *proj_list_units(void) { - return pj_get_units_ref(); -} - -const PJ_OPERATIONS *proj_list_operations(void) { - return pj_get_list_ref(); -} - -const PJ_PRIME_MERIDIANS *proj_list_prime_meridians(void) { - return pj_get_prime_meridians_ref(); -} - diff --git a/src/proj_internal.h b/src/proj_internal.h index 6571bce9..13a1cac6 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -119,7 +119,7 @@ char *pj_make_args (size_t argc, char **argv); /* Lowest level: Minimum support for fileapi */ void proj_fileapi_set (PJ *P, void *fileapi); -const char **proj_get_searchpath(void); +const char * const *proj_get_searchpath(void); int proj_get_path_count(void); size_t pj_strlcpy(char *dst, const char *src, size_t siz); diff --git a/src/proj_mdist.c b/src/proj_mdist.c index 244cf199..5f7458b5 100644 --- a/src/proj_mdist.c +++ b/src/proj_mdist.c @@ -88,7 +88,7 @@ proj_mdist_ini(double es) { } double proj_mdist(double phi, double sphi, double cphi, const void *data) { - struct MDIST *b = (struct MDIST *)data; + const struct MDIST *b = (const struct MDIST *)data; double sc, sum, sphi2, D; int i; @@ -101,7 +101,7 @@ proj_mdist(double phi, double sphi, double cphi, const void *data) { } double proj_inv_mdist(projCtx ctx, double dist, const void *data) { - struct MDIST *b = (struct MDIST *)data; + const struct MDIST *b = (const struct MDIST *)data; double s, t, phi, k; int i; diff --git a/src/projects.h b/src/projects.h index ca2cdbe3..63ea44da 100644 --- a/src/projects.h +++ b/src/projects.h @@ -713,8 +713,8 @@ double pj_qsfn_(double, PJ *); double *pj_authset(double); double pj_authlat(double, double *); -COMPLEX pj_zpoly1(COMPLEX, COMPLEX *, int); -COMPLEX pj_zpolyd1(COMPLEX, COMPLEX *, int, COMPLEX *); +COMPLEX pj_zpoly1(COMPLEX, const COMPLEX *, int); +COMPLEX pj_zpolyd1(COMPLEX, const COMPLEX *, int, COMPLEX *); int pj_deriv(LP, double, const PJ *, struct DERIVS *); int pj_factors(LP, const PJ *, double, struct FACTORS *); diff --git a/src/test228.c b/src/test228.c index 94f0ec08..57952468 100644 --- a/src/test228.c +++ b/src/test228.c @@ -17,8 +17,8 @@ int main(int argc, char* argv[]) #include #include -volatile int run = 0; -volatile int started = 0; +static volatile int run = 0; +static volatile int started = 0; static void* thread_main(void* unused) { -- cgit v1.2.3 From e073e13b4d7c830d1e7144c22a1ab1c225f47a39 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 18 Dec 2017 12:35:06 +0100 Subject: Enable wildcard globbing for MSVC builds (#714) * Enable wildcard globbing for MSVC builds * Use globbing to run all gie tests * Despite merge title: Also use wildcards on gie tests when using GNU compilers --- src/CMakeLists.txt | 9 +++++++++ src/makefile.vc | 12 ++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 494eef9b..c4f4dd20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ option(BUILD_NAD2BIN "Build nad2bin (format conversion tool)" ON) option(BUILD_PROJ "Build proj (cartographic projection tool : latlong <-> projected coordinates)" ON) if(NOT MSVC) + if (NOT APPLE) # Use relative path so that package is relocatable set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${LIBDIR}") @@ -22,6 +23,14 @@ if(NOT MSVC) # (2) setting the INSTALL_RPATH property on the executables to # "@loader_path/../${LIBDIR}" endif () + +else () + + # Linking to setargv.obj enables wildcard globbing for the + # command line utilities, when compiling with MSVC + # https://docs.microsoft.com/da-dk/cpp/c-language/expanding-wildcard-arguments + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} setargv.obj") + endif () if(BUILD_CCT) diff --git a/src/makefile.vc b/src/makefile.vc index 1155035e..c9ebd24c 100644 --- a/src/makefile.vc +++ b/src/makefile.vc @@ -102,27 +102,27 @@ proj_i.lib: $(LIBOBJ) if exist $(PROJ_DLL).manifest mt -manifest $(PROJ_DLL).manifest -outputresource:$(PROJ_DLL);2 $(PROJ_EXE): $(PROJEXE_OBJ) $(EXE_PROJ) - cl $(PROJEXE_OBJ) $(EXE_PROJ) + cl $(PROJEXE_OBJ) $(EXE_PROJ) /link setargv.obj if exist $(PROJ_EXE).manifest mt -manifest $(PROJ_EXE).manifest -outputresource:$(PROJ_EXE);1 $(CS2CS_EXE): $(CS2CSEXE_OBJ) $(EXE_PROJ) - cl $(CS2CSEXE_OBJ) $(EXE_PROJ) + cl $(CS2CSEXE_OBJ) $(EXE_PROJ) /link setargv.obj if exist $(CS2CS_EXE).manifest mt -manifest $(CS2CS_EXE).manifest -outputresource:$(CS2CS_EXE);1 $(GEOD_EXE): $(GEODEXE_OBJ) $(EXE_PROJ) - cl $(GEODEXE_OBJ) $(EXE_PROJ) + cl $(GEODEXE_OBJ) $(EXE_PROJ) /link setargv.obj if exist $(GEOD_EXE).manifest mt -manifest $(GEOD_EXE).manifest -outputresource:$(GEOD_EXE);1 $(CCT_EXE): $(CCTEXE_OBJ) $(EXE_PROJ) - cl $(CCTEXE_OBJ) $(EXE_PROJ) + cl $(CCTEXE_OBJ) $(EXE_PROJ) /link setargv.obj if exist $(CCT_EXE).manifest mt -manifest $(CCT_EXE).manifest -outputresource:$(CCT_EXE);1 $(GIE_EXE): $(GIEEXE_OBJ) $(EXE_PROJ) - cl $(GIEEXE_OBJ) $(EXE_PROJ) + cl $(GIEEXE_OBJ) $(EXE_PROJ) /link setargv.obj if exist $(GIE_EXE).manifest mt -manifest $(GIE_EXE).manifest -outputresource:$(GIE_EXE);1 $(NAD2BIN_EXE): nad2bin.obj emess.obj $(EXE_PROJ) - cl nad2bin.obj emess.obj $(EXE_PROJ) + cl nad2bin.obj emess.obj $(EXE_PROJ) /link setargv.obj $(MULTISTRESSTEST_EXE): $(MULTISTRESSTEST_OBJ) cl $(MULTISTRESSTEST_OBJ) $(EXE_PROJ) -- cgit v1.2.3 From 9eb17155ba7fe7d11110ae7e20416af0b5016750 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 18 Dec 2017 16:37:44 +0100 Subject: Remove superfluous element free_format from OPTARGPM object (#716) Fixes #699 --- src/optargpm.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/optargpm.h b/src/optargpm.h index 97755cdb..d464b5a9 100644 --- a/src/optargpm.h +++ b/src/optargpm.h @@ -216,7 +216,6 @@ struct OPTARGS { FILE *input; int input_index; int record_index; - int free_format; /* plus-style specs replaced by free format */ const char *progname; /* argv[0], stripped from /path/to, if present */ char flaglevel[21]; /* if flag -f is specified n times, its optarg pointer is set to flaglevel + n */ char *optarg[256]; /* optarg[(int) 'f'] holds a pointer to the argument of option "-f" */ @@ -415,6 +414,7 @@ const char *opt_strip_path (const char *full_name) { /* split command line options into options/flags ("-" style), projdefs ("+" style) and input file args */ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, const char **longflags, const char **longkeys) { int i, j; + int free_format; OPTARGS *o; o = (OPTARGS *) calloc (1, sizeof(OPTARGS)); @@ -424,12 +424,6 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, o->argc = argc; o->argv = argv; o->progname = opt_strip_path (argv[0]); - o->free_format = 0; - - /* Is free format in use, instead of plus-style? */ - for (i = 1; i < argc; i++) - if (0==strcmp ("--", argv[i])) - o->free_format = i; /* Reset all flags */ for (i = 0; i < (int) strlen (flags); i++) @@ -588,11 +582,20 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys, /* Process all '+'-style options, starting from where '-'-style processing ended */ o->pargv = argv + i; - if (o->free_format) { - o->pargc = o->free_format - (o->margc + 1); - o->fargc = argc - (o->free_format + 1); + + /* Is free format in use, instead of plus-style? */ + for (free_format = 0, j = 1; j < argc; j++) { + if (0==strcmp ("--", argv[j])) { + free_format = j; + break; + } + } + + if (free_format) { + o->pargc = free_format - (o->margc + 1); + o->fargc = argc - (free_format + 1); if (0 != o->fargc) - o->fargv = argv + o->free_format + 1; + o->fargv = argv + free_format + 1; return o; } -- cgit v1.2.3 From 2162ad57c190f5c924eb61fa47325c48fd894832 Mon Sep 17 00:00:00 2001 From: Charles Karney Date: Mon, 18 Dec 2017 17:56:24 -0500 Subject: Fix issue #715. Merge 1.49.1 of geodesic library (tagged as v1.49.1-c in GeographicLib). Details: Workaround bugs in handling of -0.0 in fmod and sin in Visual Studio 10, 11, and 12. Relax unrealistically strict delta for GeodSolve59 in geodtest. --- src/geodesic.c | 25 +++++++++++++++++++++++-- src/geodesic.h | 2 +- src/geodtest.c | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/geodesic.c b/src/geodesic.c index 84951d7f..233dc34c 100644 --- a/src/geodesic.c +++ b/src/geodesic.c @@ -185,8 +185,18 @@ static real AngNormalize(real x) { x = remainder(x, (real)(360)); return x != -180 ? x : 180; #else - x = fmod(x, (real)(360)); - return x <= -180 ? x + 360 : (x <= 180 ? x : x - 360); + real y = fmod(x, (real)(360)); +#if defined(_MSC_VER) && _MSC_VER < 1900 + /* + Before version 14 (2015), Visual Studio had problems dealing + with -0.0. Specifically + VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0 + sincosdx has a similar fix. + python 2.7 on Windows 32-bit machines has the same problem. + */ + if (x == 0) y = x; +#endif + return y <= -180 ? y + 360 : (y <= 180 ? y : y - 360); #endif } @@ -231,6 +241,17 @@ static void sincosdx(real x, real* sinx, real* cosx) { r *= degree; /* Possibly could call the gnu extension sincos */ s = sin(r); c = cos(r); +#if defined(_MSC_VER) && _MSC_VER < 1900 + /* + Before version 14 (2015), Visual Studio had problems dealing + with -0.0. Specifically + VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0 + VC 12 and 64-bit compile: sin(-0.0) -> +0.0 + AngNormalize has a similar fix. + python 2.7 on Windows 32-bit machines has the same problem. + */ + if (x == 0) s = x; +#endif switch ((unsigned)q & 3U) { case 0U: *sinx = s; *cosx = c; break; case 1U: *sinx = c; *cosx = -s; break; diff --git a/src/geodesic.h b/src/geodesic.h index ab18a01f..43fd0d1f 100644 --- a/src/geodesic.h +++ b/src/geodesic.h @@ -132,7 +132,7 @@ * The patch level of the geodesic library. (This tracks the version of * GeographicLib.) **********************************************************************/ -#define GEODESIC_VERSION_PATCH 0 +#define GEODESIC_VERSION_PATCH 1 /** * Pack the version components into a single integer. Users should not rely on diff --git a/src/geodtest.c b/src/geodtest.c index 5b41b72c..0f2c0ac2 100644 --- a/src/geodtest.c +++ b/src/geodtest.c @@ -505,7 +505,7 @@ static int GeodSolve59() { geod_inverse(&g, 5, 0.00000000000001, 10, 180, &s12, &azi1, &azi2); result += assertEquals(azi1, 0.000000000000035, 1.5e-14); result += assertEquals(azi2, 179.99999999999996, 1.5e-14); - result += assertEquals(s12, 18345191.174332713, 2.5e-9); + result += assertEquals(s12, 18345191.174332713, 5e-9); return result; } -- cgit v1.2.3 From a885fbb2f1f285c2cdadacdaa4616bf60184f925 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Tue, 19 Dec 2017 10:28:16 +0100 Subject: Some corrections in response to a review by Kristian Evers (#718) * Some corrections in response to a review by Kristian Evers --- src/gie.c | 1 - src/pj_init.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 2ae2ddee..8a1d356e 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1888,7 +1888,6 @@ static int pj_unitconvert_selftest (void) { ret = test_time(args3, 1e-6, in3, in3); if (ret) return ret + 30; ret = test_time(args4, 1e-6, in4, exp4); if (ret) return ret + 40; ret = test_xyz (args5, 1e-10, in5, exp5); if (ret) return ret + 50; - ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 50; ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 60; ret = test_time(args7, 1e-6, in7, in7); if (ret) return ret + 70; diff --git a/src/pj_init.c b/src/pj_init.c index 7e885575..1b0f479f 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -344,6 +344,7 @@ where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence, turning the expansion into an UTM projection on the Hayford ellipsoid. Note that 'init=foo:bar' stays in the list. It is ignored after expansion. + ******************************************************************************/ paralist *last; paralist *expn; -- cgit v1.2.3 From a70e8a3579064e80f65b1ae118ae90588142886f Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Tue, 19 Dec 2017 12:58:14 +0100 Subject: Add missing call to pj_chomp, in order to remove inline comments (#720) Previously, when expanding init=foo.bar calls, pj_chomp was first called after collecting all lines, effectively discarding everything after the start of the first comment --- src/pj_init.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 1b0f479f..310206f7 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -189,6 +189,7 @@ static char *get_init_string (PJ_CONTEXT *ctx, char *name) { /* Otherwise, handle the line. It MAY be the start of the next section, */ /* but that will be handled at the start of next trip through the loop */ buffer_length = strlen (buffer); + pj_chomp (line); /* Remove '#' style comments */ next_length = strlen (line) + buffer_length + 2; if (next_length > current_buffer_size) { char *b = pj_malloc (2 * current_buffer_size); -- cgit v1.2.3 From eadbf1520c493d7c62d62b5f0e54c3fdc26c791a Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Tue, 19 Dec 2017 16:37:10 +0100 Subject: Danish (Andrae) ellipsoid, and Copenhagen meridian introduced in support of the Reykjavik 1900 datum (Iceland) --- src/pj_datums.c | 7 ++++--- src/pj_ellps.c | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/pj_datums.c b/src/pj_datums.c index 834ce724..48c77c69 100644 --- a/src/pj_datums.c +++ b/src/pj_datums.c @@ -30,10 +30,10 @@ #define PJ_DATUMS__ #include -/* +/* * 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. + * be kept to 12 characters or less if possible. Use the official OGC + * datum name for the comments if available. */ C_NAMESPACE_VAR const struct PJ_DATUMS pj_datums[] = { @@ -86,6 +86,7 @@ C_NAMESPACE_VAR const struct PJ_PRIME_MERIDIANS pj_prime_meridians[] = { {"stockholm", "18d3'29.8\"E"}, {"athens", "23d42'58.815\"E"}, {"oslo", "10d43'22.5\"E"}, + {"copenhagen","12d34'40.35\"E"}, {NULL, NULL} }; diff --git a/src/pj_ellps.c b/src/pj_ellps.c index af5990dd..4005d1ce 100644 --- a/src/pj_ellps.c +++ b/src/pj_ellps.c @@ -16,6 +16,7 @@ pj_ellps[] = { {"NWL9D", "a=6378145.0.", "rf=298.25", "Naval Weapons Lab., 1965"}, {"mod_airy", "a=6377340.189", "b=6356034.446", "Modified Airy"}, {"andrae", "a=6377104.43", "rf=300.0", "Andrae 1876 (Den., Iclnd.)"}, +{"danish", "a=6377019.2563", "rf=300.0", "Andrae 1876 (Denmark, Iceland)"}, {"aust_SA", "a=6378160.0", "rf=298.25", "Australian Natl & S. Amer. 1969"}, {"GRS67", "a=6378160.0", "rf=298.2471674270", "GRS 67(IUGG 1967)"}, {"GSK2011", "a=6378136.5", "rf=298.2564151", "GSK-2011"}, -- cgit v1.2.3 From 15e5a2cafccca560b322dbbe93fe9e56dc94bc1d Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 20 Dec 2017 10:19:27 +0100 Subject: Avoid zero-division in ccon. Credit to OSS-Fuzz. https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=4695 --- src/PJ_ccon.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/PJ_ccon.c b/src/PJ_ccon.c index cf8a9f68..a8c178dd 100644 --- a/src/PJ_ccon.c +++ b/src/PJ_ccon.c @@ -25,12 +25,14 @@ #include #include "projects.h" +#define EPS10 1e-10 + struct pj_opaque { double phi1; double ctgphi1; double sinphi1; double cosphi1; - double *en; + double *en; }; PROJ_HEAD(ccon, "Central Conic") @@ -38,20 +40,20 @@ PROJ_HEAD(ccon, "Central Conic") -static XY forward (LP lp, PJ *P) { +static XY forward (LP lp, PJ *P) { XY xy = {0.0,0.0}; struct pj_opaque *Q = P->opaque; double r; r = Q->ctgphi1 - tan(lp.phi - Q->phi1); xy.x = r * sin(lp.lam * Q->sinphi1); - xy.y = Q->ctgphi1 - r * cos(lp.lam * Q->sinphi1); + xy.y = Q->ctgphi1 - r * cos(lp.lam * Q->sinphi1); return xy; } -static LP inverse (XY xy, PJ *P) { +static LP inverse (XY xy, PJ *P) { LP lp = {0.0,0.0}; struct pj_opaque *Q = P->opaque; @@ -63,7 +65,7 @@ static LP inverse (XY xy, PJ *P) { } -static void *destructor (PJ *P, int errlev) { +static void *destructor (PJ *P, int errlev) { if (0==P) return 0; @@ -84,9 +86,11 @@ PJ *PROJECTION(ccon) { P->destructor = destructor; Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + if (fabs(Q->phi1) < EPS10) + return destructor (P, PJD_ERR_LAT1_IS_ZERO); if (!(Q->en = pj_enfn(P->es))) - return pj_default_destructor(P, ENOMEM); + return destructor(P, ENOMEM); Q->sinphi1 = sin(Q->phi1); Q->cosphi1 = cos(Q->phi1); @@ -95,7 +99,7 @@ PJ *PROJECTION(ccon) { P->inv = inverse; P->fwd = forward; - + return P; } -- cgit v1.2.3 From 345af3c92c3ecb6d2810d09be85e3381ef66083e Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Wed, 20 Dec 2017 16:28:40 +0100 Subject: Repair column selector bug --- src/cct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cct.c b/src/cct.c index eae815e9..f315ae1a 100644 --- a/src/cct.c +++ b/src/cct.c @@ -196,7 +196,7 @@ int main(int argc, char **argv) { } if (opt_given (o, "c")) { - int ncols = sscanf (opt_arg (o, "c"), "%d,%d,%d,%d", columns_xyzt, columns_xyzt+1, columns_xyzt+3, columns_xyzt+3); + int ncols = sscanf (opt_arg (o, "c"), "%d,%d,%d,%d", columns_xyzt, columns_xyzt+1, columns_xyzt+2, columns_xyzt+3); if (ncols != nfields) { fprintf (stderr, "%s: Too few input columns given: '%s'\n", o->progname, opt_arg (o, "c")); free (o); -- cgit v1.2.3 From a3a67fb366e4628e5bda9e30b93b73648665e4d3 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Wed, 3 Jan 2018 21:06:58 +0100 Subject: Introduce preparation/finalization steps in fwd/inv subsystem, supporting arbitrary dimensionality in test code * Call trans func of same dimensionality as input in gie * Refactor prep/fin code for pj_fwd/pj_inv 2D,3D,4D * Remove prime meridian handling from pj_transform (now handled in pj_fwd_prepare/pj_inv_finalize) * Introduce prep/fin skips, mostly in support of axisswap and pipeline drivers * Refactor fwd/inv subsystem * pj_transform: Let pj_fwd/inv handle scaling * Let pj_fwd/inv3d fall back to 2D eventually --- src/Makefile.am | 2 +- src/PJ_axisswap.c | 10 +++ src/PJ_latlong.c | 99 +++++++++++++------------- src/PJ_ob_tran.c | 11 +-- src/PJ_pipeline.c | 110 +++++++++++++---------------- src/gie.c | 24 ++++++- src/lib_proj.cmake | 2 - src/makefile.vc | 1 - src/pj_fwd.c | 200 +++++++++++++++++++++++++++++++++++++++++----------- src/pj_fwd3d.c | 61 ---------------- src/pj_internal.c | 76 +++++++++++++------- src/pj_inv.c | 191 ++++++++++++++++++++++++++++++++++++++----------- src/pj_inv3d.c | 60 ---------------- src/pj_transform.c | 28 ++------ src/proj.def | 3 + src/proj_4D_api.c | 9 ++- src/proj_internal.h | 11 +++ src/projects.h | 9 ++- 18 files changed, 532 insertions(+), 375 deletions(-) delete mode 100644 src/pj_fwd3d.c delete mode 100644 src/pj_inv3d.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 096cc672..e7e53394 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,7 +70,7 @@ libproj_la_SOURCES = \ aasincos.c adjlon.c bch2bps.c bchgen.c \ biveval.c dmstor.c mk_cheby.c pj_auth.c \ pj_deriv.c pj_ell_set.c pj_ellps.c pj_errno.c \ - pj_factors.c pj_fwd.c pj_init.c pj_inv.c pj_fwd3d.c pj_inv3d.c\ + pj_factors.c pj_fwd.c pj_init.c pj_inv.c \ pj_list.c pj_malloc.c pj_mlfn.c pj_msfn.c proj_mdist.c \ pj_open_lib.c pj_param.c pj_phi2.c pj_pr_list.c \ pj_qsfn.c pj_strerrno.c \ diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index f8f17380..aee3c56e 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -173,6 +173,16 @@ PJ *CONVERSION(axisswap,0) { return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); } + /* Preparation and finalization steps are skipped, since the raison */ + /* d'etre of axisswap is to bring input coordinates in line with the */ + /* the internally expected order (ENU), such that handling of offsets */ + /* etc. can be done correctly in a later step of a pipeline */ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + + /* fill axis list with indices from 4-7 to simplify duplicate search further down */ for (i=0; i<4; i++) Q->axis[i] = i+4; diff --git a/src/PJ_latlong.c b/src/PJ_latlong.c index 5919023a..b1909954 100644 --- a/src/PJ_latlong.c +++ b/src/PJ_latlong.c @@ -30,7 +30,6 @@ /* very loosely based upon DMA code by Bradford W. Drew */ #define PJ_LIB__ #include "proj_internal.h" -#include #include "projects.h" PROJ_HEAD(lonlat, "Lat/long (Geodetic)") "\n\t"; @@ -39,87 +38,87 @@ PROJ_HEAD(latlong, "Lat/long (Geodetic alias)") "\n\t"; PROJ_HEAD(longlat, "Lat/long (Geodetic alias)") "\n\t"; - static XY forward(LP lp, PJ *P) { + static XY latlong_forward(LP lp, PJ *P) { XY xy = {0.0,0.0}; - xy.x = lp.lam / P->a; - xy.y = lp.phi / P->a; + (void) P; + xy.x = lp.lam; + xy.y = lp.phi; return xy; } -static LP inverse(XY xy, PJ *P) { +static LP latlong_inverse(XY xy, PJ *P) { LP lp = {0.0,0.0}; - lp.phi = xy.y * P->a; - lp.lam = xy.x * P->a; + (void) P; + lp.phi = xy.y; + lp.lam = xy.x; return lp; } -static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { + + static XYZ latlong_forward_3d (LPZ lpz, PJ *P) { + XYZ xyz = {0,0,0}; + (void) P; + xyz.x = lpz.lam; + xyz.y = lpz.phi; + xyz.z = lpz.z; + return xyz; +} + + +static LPZ latlong_inverse_3d (XYZ xyz, PJ *P) { + LPZ lpz = {0,0,0}; + (void) P; + lpz.lam = xyz.x; + lpz.phi = xyz.y; + lpz.z = xyz.z; + return lpz; +} + +static PJ_COORD latlong_forward_4d (PJ_COORD obs, PJ *P) { (void) P; return obs; } -static PJ_COORD inverse_4d(PJ_COORD obs, PJ *P) { + +static PJ_COORD latlong_inverse_4d (PJ_COORD obs, PJ *P) { (void) P; return obs; } -PJ *PROJECTION(latlong) { + + +static PJ *latlong_setup (PJ *P) { P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; - P->fwd = forward; - P->inv4d = inverse_4d; - P->fwd4d = forward_4d; + P->x0 = 0; + P->y0 = 0; + P->inv = latlong_inverse; + P->fwd = latlong_forward; + P->inv3d = latlong_inverse_3d; + P->fwd3d = latlong_forward_3d; + P->inv4d = latlong_inverse_4d; + P->fwd4d = latlong_forward_4d; P->left = PJ_IO_UNITS_RADIANS; P->right = PJ_IO_UNITS_RADIANS; - return P; } +PJ *PROJECTION(latlong) { + return latlong_setup (P); +} -PJ *PROJECTION(longlat) { - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; - P->fwd = forward; - P->inv4d = inverse_4d; - P->fwd4d = forward_4d; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; - return P; +PJ *PROJECTION(longlat) { + return latlong_setup (P); } PJ *PROJECTION(latlon) { - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; - P->fwd = forward; - P->inv4d = inverse_4d; - P->fwd4d = forward_4d; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; - - return P; + return latlong_setup (P); } PJ *PROJECTION(lonlat) { - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; - P->fwd = forward; - P->inv4d = inverse_4d; - P->fwd4d = forward_4d; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; - - return P; + return latlong_setup (P); } diff --git a/src/PJ_ob_tran.c b/src/PJ_ob_tran.c index 4ce4bd4d..43211fe7 100644 --- a/src/PJ_ob_tran.c +++ b/src/PJ_ob_tran.c @@ -170,11 +170,6 @@ PJ *PROJECTION(ob_tran) { P->opaque = Q; P->destructor = destructor; -#if 0 - if (0 != P->es) - return destructor(P, PJD_ERR_ELLIPSOIDAL_UNSUPPORTED); -#endif - /* get name of projection to be translated */ if (!(name = pj_param(P->ctx, P->params, "so_proj").s)) return destructor(P, PJD_ERR_NO_ROTATION_PROJ); @@ -235,6 +230,12 @@ PJ *PROJECTION(ob_tran) { P->inv = Q->link->inv ? t_inverse : 0; } + /* Support some rather speculative test cases, where the rotated projection */ + /* is actually latlong. We do not want scaling in that case... */ + if (Q->link->right==PJ_IO_UNITS_RADIANS) + P->right = PJ_IO_UNITS_METERS; + + return P; } diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index 2b012193..af9c5394 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -72,7 +72,7 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 ******************************************************************************** -* Copyright (c) 2016, Thomas Knudsen / SDFE +* Copyright (c) 2016, 2017, 2018 Thomas Knudsen / SDFE * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -96,6 +96,7 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 #define PJ_LIB__ #include +#include #include "proj_internal.h" #include "projects.h" @@ -107,7 +108,6 @@ PROJ_HEAD(pipeline, "Transformation pipeline manager"); struct pj_opaque { int reversible; int steps; - int verbose; char **argv; char **current_argv; PJ **pipeline; @@ -124,52 +124,6 @@ static LP pipeline_reverse (XY xyz, PJ *P); -/******************************************************************** - - ISOMORPHIC TRANSFORMATIONS - -********************************************************************* - - In this context, an isomorphic transformation is a proj PJ - object returning the same kind of coordinates that it - receives, i.e. a transformation from angular to angular or - linear to linear coordinates. - - The degrees-to-radians operation is an example of the former, - while the latter is typical for most of the datum shift - operations used in geodesy, e.g. the Helmert 7-parameter shift. - - Isomorphic transformations trips up the pj_inv/pj_fwd - functions, which try to check input sanity and scale output to - internal proj units, under the assumption that if input is of - angular type, then output must be of linear (or vice versa). - - Hence, to avoid having pj_inv/pj_fwd stomping on output (or - choking on input), we need a way to tell them that they should - accept whatever is being handed to them. - - The P->left and P->right elements indicate isomorphism. - - For classic proj style projections, P->left has the value - PJ_IO_UNITS_RADIANS, while P->right has the value - PJ_IO_UNITS_CLASSIC, indicating that the forward driver expects - angular input coordinates, and provides linear output coordinates, - scaled by the P->a semimajor axis length. - - Newer projections may set P->left and P->right to either - PJ_IO_UNITS_METERS, PJ_IO_UNITS_RADIANS or PJ_IO_UNITS_ANY, - to indicate their I/O style. - - For the forward driver, left indicates input coordinate - type, while right indicates output coordinate type. - - For the inverse driver, left indicates output coordinate - type, while right indicates input coordinate type. - -*********************************************************************/ - - - static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) { int i, first_step, last_step; @@ -184,7 +138,6 @@ static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) { } - static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) { int i, first_step, last_step; @@ -198,37 +151,59 @@ static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) { } -/* Delegate the work to pipeline_forward_4d() */ + + static XYZ pipeline_forward_3d (LPZ lpz, PJ *P) { PJ_COORD point = {{0,0,0,0}}; + int i; point.lpz = lpz; - point = pipeline_forward_4d (point, P); + + for (i = 1; i <= P->opaque->steps; i++) + point = pj_approx_3D_trans (P->opaque->pipeline[i], 1, point); + return point.xyz; } -/* Delegate the work to pipeline_reverse_4d() */ + static LPZ pipeline_reverse_3d (XYZ xyz, PJ *P) { PJ_COORD point = {{0,0,0,0}}; + int i; point.xyz = xyz; - point = pipeline_reverse_4d (point, P); + + for (i = P->opaque->steps; i > 0 ; i--) + point = pj_approx_3D_trans (P->opaque->pipeline[i], -1, point); + return point.lpz; } + + + static XY pipeline_forward (LP lp, PJ *P) { PJ_COORD point = {{0,0,0,0}}; + int i; point.lp = lp; - point = pipeline_forward_4d (point, P); + + for (i = 1; i <= P->opaque->steps; i++) + point = pj_approx_2D_trans (P->opaque->pipeline[i], 1, point); + return point.xy; } + static LP pipeline_reverse (XY xy, PJ *P) { PJ_COORD point = {{0,0,0,0}}; + int i; point.xy = xy; - point = pipeline_reverse_4d (point, P); + for (i = P->opaque->steps; i > 0 ; i--) + point = pj_approx_2D_trans (P->opaque->pipeline[i], -1, point); + return point.lp; } + + static void *destructor (PJ *P, int errlev) { int i; if (0==P) @@ -240,8 +215,7 @@ static void *destructor (PJ *P, int errlev) { /* Deallocate each pipeine step, then pipeline array */ if (0!=P->opaque->pipeline) for (i = 0; i < P->opaque->steps; i++) - if (0!=P->opaque->pipeline[i+1]) - P->opaque->pipeline[i+1]->destructor (P->opaque->pipeline[i+1], errlev); + proj_destroy (P->opaque->pipeline[i+1]); pj_dealloc (P->opaque->pipeline); pj_dealloc (P->opaque->argv); @@ -264,6 +238,8 @@ static PJ *pj_create_pipeline (PJ *P, size_t steps) { } + + /* count the number of args in pipeline definition */ static size_t argc_params (paralist *params) { size_t argc = 0; @@ -288,6 +264,9 @@ static char **argv_params (paralist *params, size_t argc) { return argv; } + + + /* Being the special operator that the pipeline is, we have to handle the */ /* ellipsoid differently than usual. In general, the pipeline operation does */ /* not need an ellipsoid, but in some cases it is beneficial nonetheless. */ @@ -335,18 +314,29 @@ static void set_ellipsoid(PJ *P) { } + + PJ *OPERATION(pipeline,0) { int i, nsteps = 0, argc; int i_pipeline = -1, i_first_step = -1, i_current_step; char **argv, **current_argv; - P->fwd4d = pipeline_forward_4d; - P->inv4d = pipeline_reverse_4d; + P->fwd4d = pipeline_forward_4d; + P->inv4d = pipeline_reverse_4d; P->fwd3d = pipeline_forward_3d; P->inv3d = pipeline_reverse_3d; P->fwd = pipeline_forward; P->inv = pipeline_reverse; P->destructor = destructor; + P->is_pipeline = 1; + + /* Currently, the pipeline driver is a raw bit mover, enabling other operations */ + /* to collaborate efficiently. All prep/fin stuff is done at the step levels. */ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + P->opaque = pj_calloc (1, sizeof(struct pj_opaque)); if (0==P->opaque) @@ -376,7 +366,7 @@ PJ *OPERATION(pipeline,0) { if (0==strcmp ("proj=pipeline", argv[i])) { if (-1 != i_pipeline) { - proj_log_error (P, "Pipeline: Nesting only allowed when child pipelines are wrapped in +init's"); + proj_log_error (P, "Pipeline: Nesting only allowed when child pipelines are wrapped in '+init's"); return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: nested pipelines */ } i_pipeline = i; diff --git a/src/gie.c b/src/gie.c index 8a1d356e..c6440a2d 100644 --- a/src/gie.c +++ b/src/gie.c @@ -184,6 +184,7 @@ typedef struct { int total_ok, total_ko; int grand_ok, grand_ko; size_t operation_lineno; + size_t dimensions_given, dimensions_given_at_last_accept; double tolerance; const char *curr_file; FILE *fout; @@ -581,10 +582,13 @@ Attempt to interpret args as a PJ_COORD. const char *endp, *prev = args; PJ_COORD a = proj_coord (0,0,0,0); - for (i = 0; i < 4; i++) { + for (i = 0, T.dimensions_given = 0; i < 4; i++, T.dimensions_given++) { double d = proj_strtod (prev, (char **) &endp); + + /* Break out if there were no more numerals */ if (prev==endp) return i > 1? a: proj_coord_error (); + a.v[i] = d; prev = endp; } @@ -601,6 +605,7 @@ Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. T.a = parse_coord (args); if (T.verbosity > 3) printf ("# %s\n", args); + T.dimensions_given_at_last_accept = T.dimensions_given; return 0; } @@ -693,6 +698,19 @@ static int expect_failure_with_errno_message (int expected, int got) { } +/* For test purposes, we want to call a transformation of the same */ +/* dimensionality as the number of dimensions given in accept */ +static PJ_COORD expect_trans_n_dim (PJ_COORD ci) { + if (4==T.dimensions_given_at_last_accept) + return proj_trans (T.P, T.dir, ci); + + if (3==T.dimensions_given_at_last_accept) + return pj_approx_3D_trans (T.P, T.dir, ci); + + return pj_approx_2D_trans (T.P, T.dir, ci); +} + + /*****************************************************************************/ static int expect (const char *args) { /***************************************************************************** @@ -733,7 +751,7 @@ Tell GIE what to expect, when transforming the ACCEPTed input /* Try to carry out the operation - and expect failure */ ci = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a; - co = proj_trans (T.P, T.dir, ci); + co = expect_trans_n_dim (ci); /* Failed to fail? - that's a failure */ if (co.xyz.x!=HUGE_VAL) @@ -775,7 +793,7 @@ Tell GIE what to expect, when transforming the ACCEPTed input 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 = proj_trans (T.P, T.dir, ci); + co = expect_trans_n_dim (ci); T.b = proj_angular_output (T.P, T.dir)? todeg_coord (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]); diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 88d88a97..f1337a54 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -185,7 +185,6 @@ SET(SRC_LIBPROJ_CORE pj_errno.c pj_factors.c pj_fwd.c - pj_fwd3d.c pj_gauss.c pj_gc_reader.c pj_geocent.c @@ -196,7 +195,6 @@ SET(SRC_LIBPROJ_CORE pj_init.c pj_initcache.c pj_inv.c - pj_inv3d.c pj_list.c pj_list.h pj_log.c diff --git a/src/makefile.vc b/src/makefile.vc index c9ebd24c..ee89beb3 100644 --- a/src/makefile.vc +++ b/src/makefile.vc @@ -45,7 +45,6 @@ support = \ biveval.obj dmstor.obj mk_cheby.obj pj_auth.obj \ pj_deriv.obj pj_ell_set.obj pj_ellps.obj pj_errno.obj \ pj_factors.obj pj_fwd.obj pj_init.obj pj_inv.obj \ - pj_fwd3d.obj pj_inv3d.obj \ pj_list.obj pj_malloc.obj pj_mlfn.obj pj_msfn.obj \ pj_open_lib.obj pj_param.obj pj_phi2.obj pj_pr_list.obj \ pj_qsfn.obj pj_strerrno.obj pj_tsfn.obj pj_units.obj \ diff --git a/src/pj_fwd.c b/src/pj_fwd.c index e010f6ec..a1025aac 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -1,58 +1,176 @@ -/* general forward projection */ -#define PJ_LIB__ -#include -#include -# define EPS 1.0e-12 - XY /* forward projection entry */ -pj_fwd(LP lp, PJ *P) { - XY xy; - XY err; - double t; - int last_errno; - - /* cannot const-initialize this due to MSVC's broken (non const) HUGE_VAL */ - err.x = err.y = HUGE_VAL; - - if (0==P->fwd) - return err; - last_errno = proj_errno_reset (P); +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Forward operation invocation + * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 + * Based on material from Gerald Evenden (original pj_fwd) + * and Piyush Agram (original pj_fwd3d) + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2018, Thomas Knudsen / SDFE + * + * 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. + *****************************************************************************/ +#include + +#include "proj_internal.h" +#include "projects.h" + +PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); /* Check validity of angular input coordinates */ if (P->left==PJ_IO_UNITS_RADIANS) { + double t; + LP lp = coo.lp; - /* check for forward and latitude or longitude overange */ - t = fabs(lp.phi)-M_HALFPI; - if (t > EPS || fabs(lp.lam) > 10.) { - pj_ctx_set_errno( P->ctx, -14); - return err; + /* 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) { + proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + return proj_coord_error (); } /* Clamp latitude to -90..90 degree range */ - if (fabs(t) <= EPS) - lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - else if (P->geoc) /* Maybe redundant and never used. */ - lp.phi = atan(P->rone_es * tan(lp.phi)); - lp.lam -= P->lam0; /* compute del lp.lam */ - if (!P->over) - lp.lam = adjlon(lp.lam); /* adjust del longitude */ + if (lp.phi > M_HALFPI) + lp.phi = M_HALFPI; + if (lp.phi < -M_HALFPI) + 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 */ + lp.lam = (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 = lp; } - /* Do the transformation */ - xy = (*P->fwd)(lp, P); - if ( proj_errno (P) ) - return err; + return coo; +} + + +PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { /* Classic proj.4 functions return plane coordinates in units of the semimajor axis */ if (P->right==PJ_IO_UNITS_CLASSIC) { - xy.x *= P->a; - xy.y *= P->a; + coo.xy.x *= P->a; + coo.xy.y *= P->a; } /* Handle false eastings/northings and non-metric linear units */ - xy.x = P->fr_meter * (xy.x + P->x0); - xy.y = P->fr_meter * (xy.y + P->y0); - /* z is not scaled since this is handled by vto_meter outside */ + 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); + + return coo; +} + + + +XY pj_fwd(LP lp, PJ *P) { + PJ_COORD coo = {{0,0,0,0}}; + coo.lp = lp; + + if (!P->skip_fwd_prepare) + coo = pj_fwd_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xy; + + /* Do the transformation, using the lowest dimensional transformer available */ + if (P->fwd) + coo.xy = P->fwd(coo.lp, P); + else if (P->fwd3d) + coo.xyz = P->fwd3d (coo.lpz, P); + else if (P->fwd4d) + coo = P->fwd4d (coo, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().xy; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xy; + + if (!P->skip_fwd_finalize) + coo = pj_fwd_finalize (P, coo); + return coo.xy; +} + + + +XYZ pj_fwd3d(LPZ lpz, PJ *P) { + PJ_COORD coo = {{0,0,0,0}}; + coo.lpz = lpz; + + if (!P->skip_fwd_prepare) + coo = pj_fwd_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xyz; + + /* Do the transformation, using the lowest dimensional transformer feasible */ + if (P->fwd3d) + coo.xyz = P->fwd3d(coo.lpz, P); + else if (P->fwd4d) + coo = P->fwd4d (coo, P); + else if (P->fwd) + coo.xy = P->fwd (coo.lp, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().xyz; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xyz; + + if (!P->skip_fwd_finalize) + coo = pj_fwd_finalize (P, coo); + return coo.xyz; +} + + + +PJ_COORD pj_fwd4d (PJ_COORD coo, PJ *P) { + if (!P->skip_fwd_prepare) + coo = pj_fwd_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); + + /* Call the highest dimensional converter available */ + if (P->fwd4d) + coo = P->fwd4d (coo, P); + else if (P->fwd3d) + coo.xyz = P->fwd3d (coo.lpz, P); + else if (P->fwd) + coo.xy = P->fwd (coo.lp, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error (); + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); - proj_errno_restore (P, last_errno); - return xy; + if (!P->skip_fwd_finalize) + coo = pj_fwd_finalize (P, coo); + return coo; } diff --git a/src/pj_fwd3d.c b/src/pj_fwd3d.c deleted file mode 100644 index d141178f..00000000 --- a/src/pj_fwd3d.c +++ /dev/null @@ -1,61 +0,0 @@ -#define PJ_LIB__ -#include -#include -#include -# define EPS 1.0e-12 - -/* 3D Forward transformation */ - -XYZ pj_fwd3d(LPZ lpz, PJ *P) { - XYZ xyz; - XYZ err; - double t; - int last_errno; - - /* cannot const-initialize this due to MSVC's broken (non const) HUGE_VAL */ - err.x = err.y = err.z = HUGE_VAL; - - if (0==P->fwd3d) - return err; - - last_errno = proj_errno_reset(P); - - /* Check validity of angular input coordinates */ - if (P->left==PJ_IO_UNITS_RADIANS) { - - /* check for forward and latitude or longitude overange */ - t = fabs(lpz.phi)-M_HALFPI; - if (t > EPS || fabs(lpz.lam) > 10.) { - pj_ctx_set_errno( P->ctx, -14); - return err; - } - - /* Clamp latitude to -90..90 degree range */ - if (fabs(t) <= EPS) - lpz.phi = lpz.phi < 0. ? -M_HALFPI : M_HALFPI; - else if (P->geoc) /* Maybe redundant and never used. */ - lpz.phi = atan(P->rone_es * tan(lpz.phi)); - lpz.lam -= P->lam0; /* compute del lp.lam */ - if (!P->over) - lpz.lam = adjlon(lpz.lam); /* adjust del longitude */ - } - - /* Do the transformation */ - xyz = (*P->fwd3d)(lpz, P); - if ( P->ctx->last_errno ) - return err; - - /* Classic proj.4 functions return plane coordinates in units of the semimajor axis */ - if (P->right==PJ_IO_UNITS_CLASSIC) { - xyz.x *= P->a; - xyz.y *= P->a; - } - - /* Handle false eastings/northings and non-metric linear units */ - xyz.x = P->fr_meter * (xyz.x + P->x0); - xyz.y = P->fr_meter * (xyz.y + P->y0); - /* z is not scaled since this is handled by vto_meter outside */ - - proj_errno_restore (P, last_errno); - return xyz; -} diff --git a/src/pj_internal.c b/src/pj_internal.c index 7bfd192b..88fb2b62 100644 --- a/src/pj_internal.c +++ b/src/pj_internal.c @@ -8,7 +8,7 @@ * Author: Thomas Knudsen, thokn@sdfe.dk, 2017-07-05 * ****************************************************************************** - * Copyright (c) 2016, 2017, Thomas Knudsen/SDFE + * Copyright (c) 2016, 2017, 2018, Thomas Knudsen/SDFE * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -28,16 +28,15 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PJ_INTERNAL_C -#include "proj_internal.h" -#include "projects.h" -#include - #include #include #include #include +#include +#include "proj_internal.h" +#include "projects.h" + enum pj_io_units pj_left (PJ *P) { enum pj_io_units u = P->inverted? P->right: P->left; @@ -62,32 +61,59 @@ PJ_COORD proj_coord_error (void) { } -PJ_COORD pj_fwd4d (PJ_COORD coo, PJ *P) { - if (0!=P->fwd4d) - return P->fwd4d (coo, P); - if (0!=P->fwd3d) { - coo.xyz = pj_fwd3d (coo.lpz, P); - return coo; - } - if (0!=P->fwd) { - coo.xy = pj_fwd (coo.lp, P); + +/**************************************************************************************/ +PJ_COORD pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { +/*************************************************************************************** +Behave mostly as proj_trans, but attempt to use 2D interfaces only. +Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement +chained calls starting out with a call to its 2D interface. +***************************************************************************************/ + if (0==P) return coo; + if (P->inverted) + direction = -direction; + switch (direction) { + case PJ_FWD: + coo.xy = pj_fwd (coo.lp, P); + return coo; + case PJ_INV: + coo.lp = pj_inv (coo.xy, P); + return coo; + case PJ_IDENT: + return coo; + default: + break; } proj_errno_set (P, EINVAL); return proj_coord_error (); } -PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P) { - if (0!=P->inv4d) - return P->inv4d (coo, P); - if (0!=P->inv3d) { - coo.lpz = pj_inv3d (coo.xyz, P); - return coo; - } - if (0!=P->inv) { - coo.lp = pj_inv (coo.xy, P); +/**************************************************************************************/ +PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { +/*************************************************************************************** +Companion to pj_approx_2D_trans. + +Behave mostly as proj_trans, but attempt to use 3D interfaces only. +Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement +chained calls starting out with a call to its 3D interface. +***************************************************************************************/ + if (0==P) return coo; + if (P->inverted) + direction = -direction; + switch (direction) { + case PJ_FWD: + coo.xyz = pj_fwd3d (coo.lpz, P); + return coo; + case PJ_INV: + coo.lpz = pj_inv3d (coo.xyz, P); + return coo; + case PJ_IDENT: + return coo; + default: + break; } proj_errno_set (P, EINVAL); return proj_coord_error (); @@ -96,7 +122,6 @@ PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P) { - /* Move P to a new context - or to the default context if 0 is specified */ void proj_context_set (PJ *P, PJ_CONTEXT *ctx) { if (0==ctx) @@ -105,6 +130,7 @@ void proj_context_set (PJ *P, PJ_CONTEXT *ctx) { return; } + void proj_context_inherit (PJ *parent, PJ *child) { if (0==parent) pj_set_ctx (child, pj_get_default_ctx()); diff --git a/src/pj_inv.c b/src/pj_inv.c index 68a5595b..e1fb05f6 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -1,60 +1,169 @@ -/* general inverse projection */ -#define PJ_LIB__ -#include -#include +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Inverse operation invocation + * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 + * Based on material from Gerald Evenden (original pj_inv) + * and Piyush Agram (original pj_inv3d) + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2018, Thomas Knudsen / SDFE + * + * 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. + *****************************************************************************/ #include -# define EPS 1.0e-12 -/* inverse projection entry */ -LP pj_inv(XY xy, PJ *P) { - LP lp; - LP err; - int last_errno; +#include "proj_internal.h" +#include "projects.h" - /* cannot const-initialize this due to MSVC's broken (non const) HUGE_VAL */ - err.lam = err.phi = HUGE_VAL; - if (0==P->inv) - return err; - /* can't do as much preliminary checking as with forward */ - if (xy.x == HUGE_VAL || xy.y == HUGE_VAL) { - pj_ctx_set_errno( P->ctx, -15); - return err; +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 (); } - last_errno = proj_errno_reset (P); - /* de-scale and de-offset */ - xy.x = (xy.x * P->to_meter - P->x0); - xy.y = (xy.y * P->to_meter - P->y0); + 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 depends on this */ + /* 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) { - xy.x *= P->ra; - xy.y *= P->ra; + coo.xyz.x *= P->ra; + coo.xyz.y *= P->ra; } - /* Do inverse transformation */ - lp = (*P->inv) (xy, P); - if (P->ctx->last_errno) - return err; + return coo; +} + + + +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) { - /* reduce from del lp.lam */ - lp.lam += P->lam0; + /* 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 (!P->over) - lp.lam = adjlon(lp.lam); + if (0==P->over) + coo.lpz.lam = adjlon(coo.lpz.lam); + + /* If input latitude was geocentrical, convert back to geocentrical */ + if (P->geoc) + coo = proj_geoc_lat (P, PJ_FWD, coo); + } + + return coo; +} + + + +LP pj_inv(XY xy, PJ *P) { + PJ_COORD coo = {{0,0,0,0}}; + coo.xy = xy; + + if (!P->skip_inv_prepare) + coo = pj_inv_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lp; + + /* Do the transformation, using the lowest dimensional transformer available */ + if (P->inv) + coo.lp = P->inv(coo.xy, P); + else if (P->inv3d) + coo.lpz = P->inv3d (coo.xyz, P); + else if (P->inv4d) + coo = P->inv4d (coo, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().lp; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lp; + + if (!P->skip_inv_finalize) + coo = pj_inv_finalize (P, coo); + return coo.lp; +} + + + +LPZ pj_inv3d (XYZ xyz, PJ *P) { + PJ_COORD coo = {{0,0,0,0}}; + coo.xyz = xyz; + + if (!P->skip_inv_prepare) + coo = pj_inv_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lpz; + + /* Do the transformation, using the lowest dimensional transformer feasible */ + if (P->inv3d) + coo.lpz = P->inv3d (coo.xyz, P); + else if (P->inv4d) + coo = P->inv4d (coo, P); + else if (P->inv) + coo.lp = P->inv (coo.xy, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().lpz; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lpz; + + if (!P->skip_inv_finalize) + coo = pj_inv_finalize (P, coo); + return coo.lpz; +} + + + +PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P) { + if (!P->skip_inv_prepare) + coo = pj_inv_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); - /* This may be redundant and never used */ - if (P->geoc && fabs(fabs(lp.phi)-M_HALFPI) > EPS) - lp.phi = atan(P->one_es * tan(lp.phi)); + /* Call the highest dimensional converter available */ + if (P->inv4d) + coo = P->inv4d (coo, P); + else if (P->inv3d) + coo.lpz = P->inv3d (coo.xyz, P); + else if (P->inv) + coo.lp = P->inv (coo.xy, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error (); } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); - proj_errno_restore (P, last_errno); - return lp; -} \ No newline at end of file + if (!P->skip_inv_finalize) + coo = pj_inv_finalize (P, coo); + return coo; +} diff --git a/src/pj_inv3d.c b/src/pj_inv3d.c deleted file mode 100644 index 53e39a76..00000000 --- a/src/pj_inv3d.c +++ /dev/null @@ -1,60 +0,0 @@ -#define PJ_LIB__ -#include -#include -#include -# define EPS 1.0e-12 - -/* 3D inverse transformation */ - -LPZ pj_inv3d (XYZ xyz, PJ *P) { - LPZ lpz; - LPZ err; - int last_errno; - - /* cannot const-initialize this due to MSVC's broken (non const) HUGE_VAL */ - err.lam = err.phi = err.z = HUGE_VAL; - - if (0==P->inv3d) - return err; - - /* can't do as much preliminary checking as with forward */ - if (xyz.x == HUGE_VAL || xyz.y == HUGE_VAL || xyz.z == HUGE_VAL ) { - pj_ctx_set_errno( P->ctx, -15); - return err; - } - - last_errno = proj_errno_reset (P); - - /* de-scale and de-offset */ - /* z is not de-scaled since that is handled by vto_meter before we get here */ - xyz.x = (xyz.x * P->to_meter - P->x0); - xyz.y = (xyz.y * P->to_meter - P->y0); - /* 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 depends on this */ - if (P->right==PJ_IO_UNITS_CLASSIC) { - xyz.x *= P->ra; - xyz.y *= P->ra; - } - - /* Do inverse transformation */ - lpz = (*P->inv3d) (xyz, P); - if (P->ctx->last_errno) - return err; - - if (P->left==PJ_IO_UNITS_RADIANS) { - /* reduce from del lp.lam */ - lpz.lam += P->lam0; - - /* adjust longitude to central meridian */ - if (!P->over) - lpz.lam = adjlon(lpz.lam); - - /* This may be redundant and never used */ - if (P->geoc && fabs(fabs(lpz.phi)-M_HALFPI) > EPS) - lpz.phi = atan(P->one_es * tan(lpz.phi)); - } - - proj_errno_restore (P, last_errno); - return lpz; -} diff --git a/src/pj_transform.c b/src/pj_transform.c index 21861331..c72df2e7 100644 --- a/src/pj_transform.c +++ b/src/pj_transform.c @@ -116,15 +116,6 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, return err; } -/* -------------------------------------------------------------------- */ -/* Transform Z to meters if it isn't already. */ -/* -------------------------------------------------------------------- */ - if( srcdefn->vto_meter != 1.0 && z != NULL ) - { - for( i = 0; i < point_count; i++ ) - z[point_offset*i] *= srcdefn->vto_meter; - } - /* -------------------------------------------------------------------- */ /* Transform geocentric source coordinates to lat/long. */ /* -------------------------------------------------------------------- */ @@ -145,6 +136,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, { x[point_offset*i] *= srcdefn->to_meter; y[point_offset*i] *= srcdefn->to_meter; + z[point_offset*i] *= srcdefn->to_meter; } } } @@ -253,11 +245,12 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, } } } + /* -------------------------------------------------------------------- */ /* But if they are already lat long, adjust for the prime */ /* meridian if there is one in effect. */ /* -------------------------------------------------------------------- */ - if( srcdefn->from_greenwich != 0.0 ) + if ((srcdefn->is_geocent || srcdefn->is_latlong) && ( srcdefn->from_greenwich != 0.0 )) { for( i = 0; i < point_count; i++ ) { @@ -308,15 +301,14 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, /* But if they are staying lat long, adjust for the prime */ /* meridian if there is one in effect. */ /* -------------------------------------------------------------------- */ - if( dstdefn->from_greenwich != 0.0 ) + if ((dstdefn->is_geocent || dstdefn->is_latlong) && ( dstdefn->from_greenwich != 0.0 )) { for( i = 0; i < point_count; i++ ) { if( x[point_offset*i] != HUGE_VAL ) x[point_offset*i] -= dstdefn->from_greenwich; } - } - +} /* -------------------------------------------------------------------- */ /* Transform destination latlong to geocentric if required. */ @@ -340,6 +332,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, { x[point_offset*i] *= dstdefn->fr_meter; y[point_offset*i] *= dstdefn->fr_meter; + z[point_offset*i] *= srcdefn->fr_meter; } } } @@ -453,15 +446,6 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, } } -/* -------------------------------------------------------------------- */ -/* Transform Z from meters if needed. */ -/* -------------------------------------------------------------------- */ - if( dstdefn->vto_meter != 1.0 && z != NULL ) - { - for( i = 0; i < point_count; i++ ) - z[point_offset*i] *= dstdefn->vfr_meter; - } - /* -------------------------------------------------------------------- */ /* Transform normalized axes into unusual output coordinate axis */ /* orientation if needed. */ diff --git a/src/proj.def b/src/proj.def index 51b79558..f758477d 100644 --- a/src/proj.def +++ b/src/proj.def @@ -151,3 +151,6 @@ EXPORTS pj_calc_ellipsoid_params @135 pj_chomp @136 pj_shrink @137 + + pj_approx_2D_trans @138 + pj_approx_3D_trans @139 diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index a0550727..4a416084 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -132,8 +132,15 @@ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coo) { -/* Apply the transformation P to the coordinate coo */ +/**************************************************************************************/ PJ_COORD proj_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { +/*************************************************************************************** +Apply the transformation P to the coordinate coo, preferring the 4D interfaces if +available. + +See also pj_approx_2D_trans and pj_approx_3D_trans in pj_internal.c, which work +similarly, but prefers the 2D resp. 3D interfaces if available. +***************************************************************************************/ if (0==P) return coo; if (P->inverted) diff --git a/src/proj_internal.h b/src/proj_internal.h index 13a1cac6..3dd04e62 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -57,6 +57,9 @@ extern "C" { #define PJ_TORAD(deg) ((deg)*M_PI/180.0) #endif +/* Maximum latitudinal overshoot accepted */ +#define PJ_EPS_LAT 1e-12 + /* This enum is also conditionally defined in projects.h - but we need it here */ /* for the pj_left/right prototypes, and enums cannot be forward declared */ @@ -79,6 +82,14 @@ 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); + + /* Grid functionality */ int proj_vgrid_init(PJ *P, const char *grids); int proj_hgrid_init(PJ *P, const char *grids); diff --git a/src/projects.h b/src/projects.h index 63ea44da..209d6f39 100644 --- a/src/projects.h +++ b/src/projects.h @@ -337,7 +337,12 @@ struct PJconsts { int geoc; /* Geocentric latitude flag */ int is_latlong; /* proj=latlong ... not really a projection at all */ int is_geocent; /* proj=geocent ... not really a projection at all */ + int is_pipeline; /* 1 if PJ represents a pipeline */ int need_ellps; /* 0 for operations that are purely cartesian */ + int skip_fwd_prepare; + int skip_fwd_finalize; + int skip_inv_prepare; + int skip_inv_finalize; enum pj_io_units left; /* Flags for input/output coordinate types */ enum pj_io_units right; @@ -349,7 +354,7 @@ struct PJconsts { **************************************************************************************/ - double lam0, phi0; /* central longitude, latitude */ + double lam0, phi0; /* central meridian, parallel */ double x0, y0, z0, t0; /* false easting and northing (and height and time) */ @@ -590,7 +595,7 @@ extern struct PJ_PRIME_MERIDIANS pj_prime_meridians[]; #ifdef PJ_LIB__ -#define PROJ_HEAD(id, name) static const char des_##id [] = name +#define PROJ_HEAD(name, desc) static const char des_##name [] = desc #define OPERATION(name, NEED_ELLPS) \ \ -- cgit v1.2.3 From a5ea3117194f214434a455aab9566d3f23f65f78 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Thu, 4 Jan 2018 10:43:02 +0100 Subject: Bar step-internal params from influencing pipeline driver init (#729) In cases such as: proj=pipeline step proj=utm zone=32 ellps=GRS80 ... The pj_init code would pick up the ellps information (and other material such as false eastings and northings) from the utm step and place it into the PJ object of the pipeline driver. This is not the intention, and is eliminated in this PR by terminating parameter searching (done by pj_param) once a step parameter is reached. --- src/pj_param.c | 124 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/pj_param.c b/src/pj_param.c index 5024346d..72175ed9 100644 --- a/src/pj_param.c +++ b/src/pj_param.c @@ -56,6 +56,11 @@ paralist *pj_param_exists (paralist *list, const char *parameter) { Determine whether a given parameter exists in a paralist. If it does, return a pointer to the corresponding list element - otherwise return 0. + In support of the pipeline syntax, the search is terminated once a "+step" list + element is reached, in which case a 0 is returned, unless the parameter + searched for is actually "step", in which case a pointer to the "step" list + element is returned. + This function is equivalent to the pj_param (...) call with the "opt" argument set to the parameter name preceeeded by a 't'. But by using this one, one avoids writing the code allocating memory for a new copy of parameter name, and prepending @@ -69,9 +74,12 @@ paralist *pj_param_exists (paralist *list, const char *parameter) { if (list==0) return 0; - for (next = list; next; next = next->next) + for (next = list; next; next = next->next) { if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) return next; + if (0==strcmp (parameter, "step")) + return 0; + } return 0; } @@ -92,78 +100,82 @@ paralist *pj_param_exists (paralist *list, const char *parameter) { /* `s' - string returned in PROJVALUE.s */ /* `b' - test for t/T/f/F, return in PROJVALUE.i */ /* */ +/* Search is terminated when "step" is found, in which case */ +/* 0 is returned, unless "step" was the target searched for. */ +/* */ /************************************************************************/ - PROJVALUE /* test for presence or get parameter value */ -pj_param(projCtx ctx, paralist *pl, const char *opt) { +PROJVALUE pj_param (projCtx ctx, paralist *pl, const char *opt) { int type; unsigned l; - PROJVALUE value; + PROJVALUE value = {0}; - if( ctx == NULL ) + if ( ctx == NULL ) ctx = pj_get_default_ctx(); type = *opt++; - /* simple linear lookup */ - l = (int)strlen(opt); - while (pl && !(!strncmp(pl->param, opt, l) && - (!pl->param[l] || pl->param[l] == '='))) - pl = pl->next; - if (type == 't') + + if (0==strchr ("tbirds", type)) { + fprintf(stderr, "invalid request to pj_param, fatal\n"); + exit(1); + } + + pl = pj_param_exists (pl, opt); + if (type == 't') { value.i = pl != 0; - else if (pl) { - pl->used |= 1; - opt = pl->param + l; - if (*opt == '=') - ++opt; - switch (type) { - case 'i': /* integer input */ - value.i = atoi(opt); - break; - case 'd': /* simple real input */ - value.f = pj_atof(opt); - break; - case 'r': /* degrees input */ - value.f = dmstor_ctx(ctx, opt, 0); - break; - case 's': /* char string */ - value.s = (char *) opt; - break; - case 'b': /* boolean */ - switch (*opt) { - case 'F': case 'f': - value.i = 0; - break; - case '\0': case 'T': case 't': - value.i = 1; - break; - default: - pj_ctx_set_errno(ctx, -8); - value.i = 0; - break; - } - break; - default: -bum_type: /* note: this is an error in parameter, not a user error */ - fprintf(stderr, "invalid request to pj_param, fatal\n"); - exit(1); - } - } else /* not given */ + return value; + } + + /* Not found */ + if (0==pl) { switch (type) { - case 'b': - case 'i': + case 'b': case 'i': value.i = 0; - break; - case 'd': - case 'r': + return value; + case 'd': case 'r': value.f = 0.; - break; + return value; case 's': value.s = 0; + return value; + } + } + + /* Found parameter - now find its value */ + pl->used |= 1; + l = (int) strlen(opt); + opt = pl->param + l; + if (*opt == '=') + ++opt; + + switch (type) { + case 'i': /* integer input */ + value.i = atoi(opt); + break; + case 'd': /* simple real input */ + value.f = pj_atof(opt); + break; + case 'r': /* degrees input */ + value.f = dmstor_ctx(ctx, opt, 0); + break; + case 's': /* char string */ + value.s = (char *) opt; + break; + case 'b': /* boolean */ + switch (*opt) { + case 'F': case 'f': + value.i = 0; + break; + case '\0': case 'T': case 't': + value.i = 1; break; default: - goto bum_type; + pj_ctx_set_errno (ctx, PJD_ERR_INVALID_BOOLEAN_PARAM); + value.i = 0; + break; } + break; + } return value; } -- cgit v1.2.3 From 81df68d8aa1ad9d9bb50f7266b231ec44cd36885 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 4 Jan 2018 14:54:41 +0100 Subject: Avoid overwriting time component with zero --- src/PJ_unitconvert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c index 106caabf..9cd2acf8 100644 --- a/src/PJ_unitconvert.c +++ b/src/PJ_unitconvert.c @@ -347,7 +347,7 @@ static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { Forward conversion of time units ************************************************************************/ struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD out = {{0,0,0,0}}; + PJ_COORD out = obs; /* delegate unit conversion of physical dimensions to the 3D function */ out.xyz = forward_3d(obs.lpz, P); @@ -367,7 +367,7 @@ static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { Reverse conversion of time units ************************************************************************/ struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD out = {{0,0,0,0}}; + PJ_COORD out = obs; /* delegate unit conversion of physical dimensions to the 3D function */ out.lpz = reverse_3d(obs.xyz, P); -- cgit v1.2.3 From 7449edfb10496e2e8d1633d6fe77201787ae7ad8 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 4 Jan 2018 16:51:30 +0100 Subject: Custom unit factors in unitconvert. Added the possibility to use custom unit factors. Similar to the classic +to_meter parameter the conversion factor is related to meters, i.e. the factor for conversion from kilometers meters is 1000. The custom unit factors is given using the existing xy_in, xy_out, z_in and z_out parameters, for example: proj=unitconvert xy_in=4.5 +xy_out=mm --- src/PJ_unitconvert.c | 85 ++++++++++++++++++++++++++++++++-------------------- src/gie.c | 42 ++------------------------ 2 files changed, 56 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c index 9cd2acf8..bf1fcd37 100644 --- a/src/PJ_unitconvert.c +++ b/src/PJ_unitconvert.c @@ -80,6 +80,13 @@ struct TIME_UNITS { char *name; /* comments */ }; +struct pj_opaque_unitconvert { + int t_in_id; /* time unit id for the time input unit */ + int t_out_id; /* time unit id for the time output unit */ + double xy_factor; /* unit conversion factor for horizontal components */ + double z_factor; /* unit conversion factor for vertical components */ +}; + /***********************************************************************/ static int is_leap_year(int year) { @@ -255,7 +262,6 @@ static double mjd_to_yyyymmdd(double mjd) { return year*10000.0 + month*100.0 + day; } - static const struct TIME_UNITS time_units[] = { {"mjd", mjd_to_mjd, mjd_to_mjd, "Modified julian date"}, {"decimalyear", decimalyear_to_mjd, mjd_to_decimalyear, "Decimal year"}, @@ -264,15 +270,6 @@ static const struct TIME_UNITS time_units[] = { {NULL, NULL, NULL, NULL} }; -struct pj_opaque_unitconvert { - int t_in_id; /* time unit id for the time input unit */ - int t_out_id; /* time unit id for the time output unit */ - int xy_in_id; /* unit id for the horizontal input unit */ - int xy_out_id; /* unit id for the horizontal output unit */ - int z_in_id; /* unit id for the vertical input unit */ - int z_out_id; /* unit id for the vertical output unit */ -}; - /***********************************************************************/ static XY forward_2d(LP lp, PJ *P) { @@ -283,8 +280,8 @@ static XY forward_2d(LP lp, PJ *P) { PJ_COORD point = {{0,0,0,0}}; point.lp = lp; - point.xy.x *= pj_units[Q->xy_in_id].factor / pj_units[Q->xy_out_id].factor; - point.xy.y *= pj_units[Q->xy_in_id].factor / pj_units[Q->xy_out_id].factor; + point.xy.x *= Q->xy_factor; + point.xy.y *= Q->xy_factor; return point.xy; } @@ -299,8 +296,8 @@ static LP reverse_2d(XY xy, PJ *P) { PJ_COORD point = {{0,0,0,0}}; point.xy = xy; - point.xy.x *= pj_units[Q->xy_out_id].factor / pj_units[Q->xy_in_id].factor; - point.xy.y *= pj_units[Q->xy_out_id].factor / pj_units[Q->xy_in_id].factor; + point.xy.x /= Q->xy_factor; + point.xy.y /= Q->xy_factor; return point.lp; } @@ -318,7 +315,7 @@ static XYZ forward_3d(LPZ lpz, PJ *P) { /* take care of the horizontal components in the 2D function */ point.xy = forward_2d(point.lp, P); - point.xyz.z *= pj_units[Q->z_in_id].factor / pj_units[Q->z_out_id].factor; + point.xyz.z *= Q->z_factor; return point.xyz; } @@ -335,7 +332,7 @@ static LPZ reverse_3d(XYZ xyz, PJ *P) { /* take care of the horizontal components in the 2D function */ point.lp = reverse_2d(point.xy, P); - point.xyz.z *= pj_units[Q->z_out_id].factor / pj_units[Q->z_in_id].factor; + point.xyz.z /= Q->z_factor; return point.lpz; } @@ -387,6 +384,7 @@ PJ *CONVERSION(unitconvert,0) { struct pj_opaque_unitconvert *Q = pj_calloc (1, sizeof (struct pj_opaque_unitconvert)); char *s, *name; int i; + double f; if (0==Q) return pj_default_destructor (P, ENOMEM); @@ -406,40 +404,63 @@ PJ *CONVERSION(unitconvert,0) { Q->t_in_id = -1; Q->t_out_id = -1; + Q->xy_factor = 1.0; + Q->z_factor = 1.0; + if ((name = pj_param (P->ctx, P->params, "sxy_in").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i); - if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); - - Q->xy_in_id = i; - proj_log_debug(P, "xy_in unit: %s", pj_units[i].name); + if (s) { + f = pj_units[i].factor; + proj_log_debug(P, "xy_in unit: %s", pj_units[i].name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dxy_in").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); + } + if (f != 0.0) + Q->xy_factor *= f; } if ((name = pj_param (P->ctx, P->params, "sxy_out").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i); - if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); - - Q->xy_out_id = i; - proj_log_debug(P, "xy_out unit: %s", pj_units[i].name); + if (s) { + f = pj_units[i].factor; + proj_log_debug(P, "xy_out unit: %s", pj_units[i].name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dxy_out").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); + } + if (f != 0.0) + Q->xy_factor /= f; } if ((name = pj_param (P->ctx, P->params, "sz_in").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i); - if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); /* unknown unit conversion id */ - - Q->z_in_id = i; - proj_log_debug(P, "z_in unit: %s", pj_units[i].name); + if (s) { + f = pj_units[i].factor; + proj_log_debug(P, "z_in unit: %s", pj_units[i].name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dz_in").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); + } + if (f != 0.0) + Q->z_factor *= f; } if ((name = pj_param (P->ctx, P->params, "sz_out").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i); - if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); /* unknown unit conversion id */ - - Q->z_out_id = i; - proj_log_debug(P, "z_out unit: %s", pj_units[i].name); + if (s) { + f = pj_units[i].factor; + proj_log_debug(P, "z_out unit: %s", pj_units[i].name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dz_out").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOW_UNIT_ID); + } + if (f != 0.0) + Q->z_factor /= f; } diff --git a/src/gie.c b/src/gie.c index c6440a2d..e37acf62 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1850,34 +1850,6 @@ static int test_time(const char* args, double tol, double t_in, double t_exp) { return ret; } -static int test_xyz(const char* args, double tol, PJ_COORD in, PJ_COORD exp) { - PJ_COORD out = {{0,0,0,0}}, obs_in = {{0,0,0,0}}; - PJ *P = proj_create(PJ_DEFAULT_CTX, args); - int ret = 0; - - if (P == 0) - return 5; - - obs_in.xyz = in.xyz; - out = proj_trans(P, PJ_FWD, obs_in); - if (proj_xyz_dist(out.xyz, exp.xyz) > tol) { - printf("exp: %10.10g, %10.10g, %10.10g\n", exp.xyz.x, exp.xyz.y, exp.xyz.z); - printf("out: %10.10g, %10.10g, %10.10g\n", out.xyz.x, out.xyz.y, out.xyz.z); - ret = 1; - } - - out = proj_trans(P, PJ_INV, out); - if (proj_xyz_dist(out.xyz, in.xyz) > tol) { - printf("exp: %g, %g, %g\n", in.xyz.x, in.xyz.y, in.xyz.z); - printf("out: %g, %g, %g\n", out.xyz.x, out.xyz.y, out.xyz.z); - ret += 2; - } - proj_destroy(P); - proj_log_level(NULL, 0); - return ret; -} - - static int pj_unitconvert_selftest (void) { int ret = 0; char args1[] = "+proj=unitconvert +t_in=decimalyear +t_out=decimalyear"; @@ -1892,22 +1864,14 @@ static int pj_unitconvert_selftest (void) { char args4[] = "+proj=unitconvert +t_in=gps_week +t_out=decimalyear"; double in4 = 1877.71428, exp4 = 2016.0; - char args5[] = "+proj=unitconvert +xy_in=m +xy_out=dm +z_in=cm +z_out=mm"; - PJ_COORD in5 = {{55.25, 23.23, 45.5, 0}}, exp5 = {{552.5, 232.3, 455.0, 0}}; - - char args6[] = "+proj=unitconvert +xy_in=m +xy_out=m +z_in=m +z_out=m"; - PJ_COORD in6 = {{12.3, 45.6, 7.89, 0}}; - - char args7[] = "+proj=unitconvert +t_in=yyyymmdd +t_out=yyyymmdd"; - double in7 = 20170131; + char args5[] = "+proj=unitconvert +t_in=yyyymmdd +t_out=yyyymmdd"; + double in5 = 20170131; ret = test_time(args1, 1e-6, in1, in1); if (ret) return ret + 10; ret = test_time(args2, 1e-6, in2, in2); if (ret) return ret + 20; ret = test_time(args3, 1e-6, in3, in3); if (ret) return ret + 30; ret = test_time(args4, 1e-6, in4, exp4); if (ret) return ret + 40; - ret = test_xyz (args5, 1e-10, in5, exp5); if (ret) return ret + 50; - ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 60; - ret = test_time(args7, 1e-6, in7, in7); if (ret) return ret + 70; + ret = test_time(args5, 1e-6, in5, in5); if (ret) return ret + 50; return 0; -- cgit v1.2.3 From 51a2ab2d6706d39c81a1555608ba8954068e2543 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 6 Jan 2018 11:30:27 +0100 Subject: Eliminate potential zero-dereference in get_init. --- src/pj_init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 310206f7..2cf53e41 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -246,7 +246,8 @@ Expand key from buffer or (if not in buffer) from init file if (0==definition) return 0; init_items = string_to_paralist (ctx, definition); - pj_log (ctx, 3, "get_init: got [%s], paralist[0,1]: [%s,%s]\n", definition, init_items->param, init_items->next? init_items->next->param: "(empty)"); + if (init_items) + pj_log (ctx, 3, "get_init: got [%s], paralist[0,1]: [%s,%s]\n", definition, init_items->param, init_items->next? init_items->next->param: "(empty)"); pj_dealloc (definition); if (0==init_items) return 0; -- cgit v1.2.3 From 81885b034d06191d7e1bc7c96a88dd4ceb37fed4 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 6 Jan 2018 13:17:41 +0100 Subject: Repair ratio numbers for units in pj_init_ctx --- src/pj_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 2cf53e41..087a3096 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -691,9 +691,9 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { int ratio = 0; /* ratio number? */ - if (*s == '/') { + if (strlen (s) > 1 && s[0] == '1' && s[1]=='/') { ratio = 1; - s++; + s += 2; } factor = pj_strtod(s, &s); -- cgit v1.2.3 From 42e3c6c2ae465ff3dd7397afe5487513b170e7cf Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 6 Jan 2018 23:09:57 +0100 Subject: Remove superfluous self-assignment in phi12 --- src/PJ_sconics.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/PJ_sconics.c b/src/PJ_sconics.c index 93a2f2f4..b94fca12 100644 --- a/src/PJ_sconics.c +++ b/src/PJ_sconics.c @@ -52,7 +52,6 @@ static int phi12(PJ *P, double *del) { *del = 0.5 * (p2 - p1); P->opaque->sig = 0.5 * (p2 + p1); err = (fabs(*del) < EPS || fabs(P->opaque->sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0; - *del = *del; } return err; } -- cgit v1.2.3 From 4809a281abca09085cba6494a0da247c03f0a487 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 6 Jan 2018 23:14:29 +0100 Subject: Remove unused typedef from pj_init.c --- src/pj_init.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 087a3096..862a6515 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -471,7 +471,6 @@ pj_init(int argc, char **argv) { typedef PJ *(constructor)(PJ *); -typedef constructor *(*function_returning_constructor)(const char *); static constructor *pj_constructor (const char *name) { int i; -- cgit v1.2.3 From 07bb446ec474cf62094070a8ea848b37c911a075 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 8 Jan 2018 03:41:45 +0100 Subject: Discern between 2D projected and 3D cartesian linear units (#732) --- src/PJ_axisswap.c | 5 ++--- src/PJ_cart.c | 3 +-- src/PJ_deformation.c | 4 ++-- src/PJ_helmert.c | 10 ++++++++-- src/PJ_horner.c | 2 +- src/PJ_ob_tran.c | 2 +- src/PJ_unitconvert.c | 5 ++--- src/pj_internal.c | 12 ++++++------ src/proj_internal.h | 12 +++++++----- src/projects.h | 8 +++++--- 10 files changed, 35 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index aee3c56e..62a9fba3 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -242,10 +242,9 @@ PJ *CONVERSION(axisswap,0) { P->left = PJ_IO_UNITS_RADIANS; P->right = PJ_IO_UNITS_RADIANS; } else { - P->left = PJ_IO_UNITS_METERS; - P->right = PJ_IO_UNITS_METERS; + P->left = PJ_IO_UNITS_PROJECTED; + P->right = PJ_IO_UNITS_PROJECTED; } return P; } - diff --git a/src/PJ_cart.c b/src/PJ_cart.c index 12b5876a..8e11c030 100644 --- a/src/PJ_cart.c +++ b/src/PJ_cart.c @@ -214,7 +214,6 @@ PJ *CONVERSION(cart,1) { P->fwd = cart_forward; P->inv = cart_reverse; P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_METERS; + P->right = PJ_IO_UNITS_CARTESIAN; return P; } - diff --git a/src/PJ_deformation.c b/src/PJ_deformation.c index d227e49f..797af006 100644 --- a/src/PJ_deformation.c +++ b/src/PJ_deformation.c @@ -306,8 +306,8 @@ PJ *TRANSFORMATION(deformation,1) { P->fwd = 0; P->inv = 0; - P->left = PJ_IO_UNITS_METERS; - P->right = PJ_IO_UNITS_METERS; + P->left = PJ_IO_UNITS_CARTESIAN; + P->right = PJ_IO_UNITS_CARTESIAN; P->destructor = destructor; return P; diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c index ca14a5cb..f726d946 100644 --- a/src/PJ_helmert.c +++ b/src/PJ_helmert.c @@ -477,8 +477,14 @@ PJ *TRANSFORMATION(helmert, 0) { P->fwd = helmert_forward; P->inv = helmert_reverse; - P->left = PJ_IO_UNITS_METERS; - P->right = PJ_IO_UNITS_METERS; + /* In most cases, we work on 3D cartesian coordinates */ + P->left = PJ_IO_UNITS_CARTESIAN; + P->right = PJ_IO_UNITS_CARTESIAN; + /* But in the 2D case, the coordinates are projected */ + if (pj_param_exists (P->params, "theta")) { + P->left = PJ_IO_UNITS_PROJECTED; + P->right = PJ_IO_UNITS_PROJECTED; + } /* Translations */ if (pj_param (P->ctx, P->params, "tx").i) diff --git a/src/PJ_horner.c b/src/PJ_horner.c index fe0452d6..d1146aa1 100644 --- a/src/PJ_horner.c +++ b/src/PJ_horner.c @@ -434,7 +434,7 @@ PJ *PROJECTION(horner) { P->inv3d = 0; P->fwd = 0; P->inv = 0; - P->left = P->right = PJ_IO_UNITS_METERS; + P->left = P->right = PJ_IO_UNITS_PROJECTED; P->destructor = horner_freeup; /* Polynomial degree specified? */ diff --git a/src/PJ_ob_tran.c b/src/PJ_ob_tran.c index 43211fe7..debb211c 100644 --- a/src/PJ_ob_tran.c +++ b/src/PJ_ob_tran.c @@ -233,7 +233,7 @@ PJ *PROJECTION(ob_tran) { /* Support some rather speculative test cases, where the rotated projection */ /* is actually latlong. We do not want scaling in that case... */ if (Q->link->right==PJ_IO_UNITS_RADIANS) - P->right = PJ_IO_UNITS_METERS; + P->right = PJ_IO_UNITS_PROJECTED; return P; diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c index bf1fcd37..06224362 100644 --- a/src/PJ_unitconvert.c +++ b/src/PJ_unitconvert.c @@ -397,8 +397,8 @@ PJ *CONVERSION(unitconvert,0) { P->fwd = forward_2d; P->inv = reverse_2d; - P->left = PJ_IO_UNITS_METERS; - P->right = PJ_IO_UNITS_METERS; + P->left = PJ_IO_UNITS_WHATEVER; + P->right = PJ_IO_UNITS_WHATEVER; /* if no time input/output unit is specified we can skip them */ Q->t_in_id = -1; @@ -485,4 +485,3 @@ PJ *CONVERSION(unitconvert,0) { return P; } - diff --git a/src/pj_internal.c b/src/pj_internal.c index 88fb2b62..deb0c054 100644 --- a/src/pj_internal.c +++ b/src/pj_internal.c @@ -40,16 +40,16 @@ enum pj_io_units pj_left (PJ *P) { enum pj_io_units u = P->inverted? P->right: P->left; - if (u==PJ_IO_UNITS_RADIANS) - return PJ_IO_UNITS_RADIANS; - return PJ_IO_UNITS_METERS; + if (u==PJ_IO_UNITS_CLASSIC) + return PJ_IO_UNITS_PROJECTED; + return u; } enum pj_io_units pj_right (PJ *P) { enum pj_io_units u = P->inverted? P->left: P->right; - if (u==PJ_IO_UNITS_RADIANS) - return PJ_IO_UNITS_RADIANS; - return PJ_IO_UNITS_METERS; + if (u==PJ_IO_UNITS_CLASSIC) + return PJ_IO_UNITS_PROJECTED; + return u; } diff --git a/src/proj_internal.h b/src/proj_internal.h index 3dd04e62..41f95c07 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -61,12 +61,14 @@ extern "C" { #define PJ_EPS_LAT 1e-12 -/* This enum is also conditionally defined in projects.h - but we need it here */ -/* for the pj_left/right prototypes, and enums cannot be forward declared */ +/* This enum is also conditionally defined in projects.h - but enums cannot */ +/* be forward declared and we need it here for the pj_left/right prototypes */ enum pj_io_units { - PJ_IO_UNITS_CLASSIC = 0, /* Scaled meters (right) */ - PJ_IO_UNITS_METERS = 1, /* Meters */ - PJ_IO_UNITS_RADIANS = 2 /* Radians */ + PJ_IO_UNITS_WHATEVER = 0, /* Doesn't matter (or depends on pipeline neighbours) */ + PJ_IO_UNITS_CLASSIC = 1, /* Scaled meters (right), projected system */ + PJ_IO_UNITS_PROJECTED = 2, /* Meters, projected system */ + PJ_IO_UNITS_CARTESIAN = 3, /* Meters, 3D cartesian system */ + PJ_IO_UNITS_RADIANS = 4 /* Radians */ }; enum pj_io_units pj_left (PJ *P); enum pj_io_units pj_right (PJ *P); diff --git a/src/projects.h b/src/projects.h index 209d6f39..86ca2524 100644 --- a/src/projects.h +++ b/src/projects.h @@ -192,9 +192,11 @@ typedef struct PJ_REGION_S PJ_Region; typedef struct ARG_list paralist; /* parameter list */ #ifndef PROJ_INTERNAL_H enum pj_io_units { - PJ_IO_UNITS_CLASSIC = 0, /* Scaled meters (right) */ - PJ_IO_UNITS_METERS = 1, /* Meters */ - PJ_IO_UNITS_RADIANS = 2 /* Radians */ + PJ_IO_UNITS_WHATEVER = 0, /* Doesn't matter (or depends on pipeline neighbours) */ + PJ_IO_UNITS_CLASSIC = 1, /* Scaled meters (right), projected system */ + PJ_IO_UNITS_PROJECTED = 2, /* Meters, projected system */ + PJ_IO_UNITS_CARTESIAN = 3, /* Meters, 3D cartesian system */ + PJ_IO_UNITS_RADIANS = 4 /* Radians */ }; #endif #ifndef PROJ_H -- cgit v1.2.3 From 47953f0a93cd90d0d52b74f2b2705516f018b2f1 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Mon, 8 Jan 2018 11:54:54 +0100 Subject: Allow usage of classic +axis parameter in axisswap. Instead of +order the classic PROJ.4 parameter +axis can used instead. This is mostly an inititive to simplify backwars compatibility in the 4D API. P->axis is initialized in pj_init() it can be assumed that it is set up correctly. +order and +axis are mutually exclusive. --- src/PJ_axisswap.c | 97 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index 62a9fba3..bb22f41e 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -159,53 +159,70 @@ static PJ_COORD reverse_4d(PJ_COORD coo, PJ *P) { PJ *CONVERSION(axisswap,0) { /***********************************************************************/ struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - char *order, *s; - unsigned int i, j, n; + char *s; + unsigned int i, j, n = 0; if (0==Q) return pj_default_destructor (P, ENOMEM); P->opaque = (void *) Q; - /* read axis order */ - if (pj_param (P->ctx, P->params, "torder").i) { - order = pj_param(P->ctx, P->params, "sorder").s; - } else { - return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); - } - - /* Preparation and finalization steps are skipped, since the raison */ - /* d'etre of axisswap is to bring input coordinates in line with the */ - /* the internally expected order (ENU), such that handling of offsets */ - /* etc. can be done correctly in a later step of a pipeline */ - P->skip_fwd_prepare = 1; - P->skip_fwd_finalize = 1; - P->skip_inv_prepare = 1; - P->skip_inv_finalize = 1; + /* +order and +axis are mutually exclusive */ + if ( !pj_param_exists(P->params, "order") == !pj_param_exists(P->params, "axis") ) + return pj_default_destructor(P, PJD_ERR_AXIS); /* fill axis list with indices from 4-7 to simplify duplicate search further down */ - for (i=0; i<4; i++) + for (i=0; i<4; i++) { Q->axis[i] = i+4; + Q->sign[i] = 1; + } + + /* if the "order" parameter is used */ + if ( pj_param_exists(P->params, "order") ) { + /* read axis order */ + char *order = pj_param(P->ctx, P->params, "sorder").s; + + /* check that all characters are valid */ + for (i=0; iaxis[n] = abs(atoi(s))-1; + Q->sign[n++] = sign(atoi(s)); + while ( *s != '\0' && *s != ',' ) + s++; + if ( *s == ',' ) + s++; } + } - /* read axes numbers and signs */ - for ( s = order, n = 0; *s != '\0' && n < 4; ) { - Q->axis[n] = abs(atoi(s))-1; - if (Q->axis[n] >= 4) { - proj_log_error(P, "swapaxis: invalid axis '%s'", s); - return pj_default_destructor(P, PJD_ERR_AXIS); + /* if the "axis" parameter is used */ + if ( pj_param_exists(P->params, "axis") ) { + /* parse the classic PROJ.4 enu axis specification */ + for (i=0; i < 3; i++) { + switch(P->axis[i]) { + case 'w': + Q->sign[i] = -1; + case 'e': + Q->axis[i] = 0; + break; + case 's': + Q->sign[i] = -1; + case 'n': + Q->axis[i] = 1; + break; + case 'd': + Q->sign[i] = -1; + case 'u': + Q->axis[i] = 2; + break; + } } - Q->sign[n++] = sign(atoi(s)); - while ( *s != '\0' && *s != ',' ) - s++; - if ( *s == ',' ) - s++; + n = 3; } /* check for duplicate axes */ @@ -219,6 +236,7 @@ PJ *CONVERSION(axisswap,0) { } } + /* only map fwd/inv functions that are possible with the given axis setup */ if (n == 4) { P->fwd4d = forward_4d; @@ -233,6 +251,7 @@ PJ *CONVERSION(axisswap,0) { P->inv = reverse_2d; } + if (P->fwd4d == NULL && P->fwd3d == NULL && P->fwd == NULL) { proj_log_error(P, "swapaxis: bad axis order"); return pj_default_destructor(P, PJD_ERR_AXIS); @@ -246,5 +265,15 @@ PJ *CONVERSION(axisswap,0) { P->right = PJ_IO_UNITS_PROJECTED; } + + /* Preparation and finalization steps are skipped, since the raison */ + /* d'etre of axisswap is to bring input coordinates in line with the */ + /* the internally expected order (ENU), such that handling of offsets */ + /* etc. can be done correctly in a later step of a pipeline */ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + return P; } -- cgit v1.2.3 From f7a1fe14454b091eecb5fd3e33ff73796a726da1 Mon Sep 17 00:00:00 2001 From: Aaron Puchert Date: Tue, 9 Jan 2018 20:32:08 +0100 Subject: Remove or use unused macros Some macros seemed to be leftover from earlier code, so I removed them. Others seemed like they should have been used, but weren't. There should be no functional change, except the following: in floating- point arithmetic, x / y is not the same as x * (1.0 / y). It can be argued that using the multiplication is significantly faster, and the algorithm is approximative anyway. Otherwise, the constants are obviously not required. Also fixes one location in PJ_aitoff.c, where an enumeration value should have been used. --- src/PJ_aitoff.c | 2 +- src/PJ_boggs.c | 1 - src/PJ_cass.c | 1 - src/PJ_eck4.c | 4 ++-- src/PJ_isea.c | 10 +--------- src/PJ_laea.c | 2 -- src/PJ_sterea.c | 2 -- src/PJ_vandg.c | 1 - src/aasincos.c | 1 - src/gen_cheb.c | 4 ++-- src/gie.c | 3 --- src/proj_etmerc.c | 1 - src/proj_mdist.c | 2 +- 13 files changed, 7 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/PJ_aitoff.c b/src/PJ_aitoff.c index 8927d39c..c4a5abdc 100644 --- a/src/PJ_aitoff.c +++ b/src/PJ_aitoff.c @@ -141,7 +141,7 @@ static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ y *= D * sin(lp.phi); } else x = y = 0.; - if (Q->mode) { /* Winkel Tripel */ + if (Q->mode == WINKEL_TRIPEL) { x = (x + lp.lam * Q->cosphi1) * 0.5; y = (y + lp.phi) * 0.5; } diff --git a/src/PJ_boggs.c b/src/PJ_boggs.c index fe17eaa6..93de0679 100644 --- a/src/PJ_boggs.c +++ b/src/PJ_boggs.c @@ -4,7 +4,6 @@ PROJ_HEAD(boggs, "Boggs Eumorphic") "\n\tPCyl., no inv., Sph."; # define NITER 20 # define EPS 1e-7 -# define ONETOL 1.000001 # define FXC 2.00276 # define FXC2 1.11072 # define FYC 0.49931 diff --git a/src/PJ_cass.c b/src/PJ_cass.c index ddb3eaf3..acf779a1 100644 --- a/src/PJ_cass.c +++ b/src/PJ_cass.c @@ -4,7 +4,6 @@ PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell"; -# define EPS10 1e-10 # define C1 .16666666666666666666 # define C2 .00833333333333333333 # define C3 .04166666666666666666 diff --git a/src/PJ_eck4.c b/src/PJ_eck4.c index c3e1b677..a95c10b5 100644 --- a/src/PJ_eck4.c +++ b/src/PJ_eck4.c @@ -44,9 +44,9 @@ static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ LP lp = {0.0,0.0}; double c; - lp.phi = aasin(P->ctx,xy.y / C_y); + lp.phi = aasin(P->ctx,xy.y * RC_y); lp.lam = xy.x / (C_x * (1. + (c = cos(lp.phi)))); - lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) / C_p); + lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) * RC_p); return lp; } diff --git a/src/PJ_isea.c b/src/PJ_isea.c index 9baea8b5..5cc71c08 100644 --- a/src/PJ_isea.c +++ b/src/PJ_isea.c @@ -170,10 +170,6 @@ struct snyder_constants constants[] = { {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0}, }; -#define E 52.62263186 -#define F 10.81231696 - -#define DEG60 1.04719755119659774614 #define DEG120 2.09439510239319549229 #define DEG72 1.25663706143591729537 #define DEG90 1.57079632679489661922 @@ -266,14 +262,12 @@ az_adjustment(int triangle) /* H = 0.25 R tan g = */ #define TABLE_H 0.1909830056 -#define RPRIME 0.91038328153090290025 - ISEA_STATIC struct isea_pt isea_triangle_xy(int triangle) { struct isea_pt c; - double Rprime = 0.91038328153090290025; + const double Rprime = 0.91038328153090290025; triangle = (triangle - 1) % 20; @@ -509,8 +503,6 @@ isea_snyder_forward(struct isea_geo * ll, struct isea_pt * out) * in original coordinate system, this function return the new coordinates. */ -#define PRECISION 0.0000000000005 - /* formula from Snyder, Map Projections: A working manual, p31 */ /* * old north pole at np in new coordinates diff --git a/src/PJ_laea.c b/src/PJ_laea.c index 82a341d8..9edd5558 100644 --- a/src/PJ_laea.c +++ b/src/PJ_laea.c @@ -26,8 +26,6 @@ struct pj_opaque { }; #define EPS10 1.e-10 -#define NITER 20 -#define CONV 1.e-10 static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ XY xy = {0.0,0.0}; diff --git a/src/PJ_sterea.c b/src/PJ_sterea.c index 38b26117..b1cb736a 100644 --- a/src/PJ_sterea.c +++ b/src/PJ_sterea.c @@ -37,8 +37,6 @@ struct pj_opaque { PROJ_HEAD(sterea, "Oblique Stereographic Alternative") "\n\tAzimuthal, Sph&Ell"; -# define DEL_TOL 1.e-14 -# define MAX_ITER 10 diff --git a/src/PJ_vandg.c b/src/PJ_vandg.c index b6b84bd0..d4ed7b8a 100644 --- a/src/PJ_vandg.c +++ b/src/PJ_vandg.c @@ -6,7 +6,6 @@ PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph"; # define TOL 1.e-10 # define THIRD .33333333333333333333 -# define TWO_THRD .66666666666666666666 # define C2_27 .07407407407407407407 # define PI4_3 4.18879020478639098458 # define PISQ 9.86960440108935861869 diff --git a/src/aasincos.c b/src/aasincos.c index 7cb6bd89..7d49ac1a 100644 --- a/src/aasincos.c +++ b/src/aasincos.c @@ -1,7 +1,6 @@ /* arc sin, cosine, tan2 and sqrt that will NOT fail */ #include #define ONE_TOL 1.00000000000001 -#define TOL 0.000000001 #define ATOL 1e-50 double diff --git a/src/gen_cheb.c b/src/gen_cheb.c index 9b2d8cb3..351d9604 100644 --- a/src/gen_cheb.c +++ b/src/gen_cheb.c @@ -6,7 +6,7 @@ #include #include "emess.h" #ifndef COEF_LINE_MAX -#define COEF_LINE_MAX 60 +#define COEF_LINE_MAX 50 #endif /* FIXME: put the declaration in a header. Also used in proj.c */ @@ -43,7 +43,7 @@ void gen_cheb(int inverse, projUV (*proj)(projUV), char *s, PJ *P, if (*arg != '+') { if (!n) { putchar('#'); ++n; } (void)printf(" %s%n",arg, &L); - if ((n += L) > 50) { putchar('\n'); n = 0; } + if ((n += L) > COEF_LINE_MAX) { putchar('\n'); n = 0; } } } if (n) putchar('\n'); diff --git a/src/gie.c b/src/gie.c index e37acf62..e0045ebd 100644 --- a/src/gie.c +++ b/src/gie.c @@ -198,9 +198,6 @@ static gie_ctx T; static const char delim[] = {"-------------------------------------------------------------------------------\n"}; -#define CMDLEN 250000 - - static const char usage[] = { "--------------------------------------------------------------------------------\n" "Usage: %s [-options]... infile...\n" diff --git a/src/proj_etmerc.c b/src/proj_etmerc.c index b03862f8..67da4981 100644 --- a/src/proj_etmerc.c +++ b/src/proj_etmerc.c @@ -39,7 +39,6 @@ */ -#define PROJ_LIB__ #define PJ_LIB__ #include diff --git a/src/proj_mdist.c b/src/proj_mdist.c index 5f7458b5..24957895 100644 --- a/src/proj_mdist.c +++ b/src/proj_mdist.c @@ -27,7 +27,7 @@ ** and inverse on unit ellipsoid. ** Precision commensurate with double precision. */ -#define PROJ_LIB__ +#define PJ_LIB__ #include #define MAX_ITER 20 #define TOL 1e-14 -- cgit v1.2.3 From d109f6e826e7a2542d7e1bd50cc62e5fa9ac373e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 11 Jan 2018 12:13:47 +0100 Subject: PJ_axisswap.c: validate axis value. Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=5073. Credit to OSS Fuzz. master only --- src/PJ_axisswap.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index bb22f41e..0d1683b4 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -192,6 +192,10 @@ PJ *CONVERSION(axisswap,0) { /* read axes numbers and signs */ for ( s = order, n = 0; *s != '\0' && n < 4; ) { Q->axis[n] = abs(atoi(s))-1; + if (Q->axis[n] <= 0 || Q->axis[n] > 3) { + proj_log_error(P, "axisswap: invalid axis '%d'", Q->axis[n]); + return pj_default_destructor(P, PJD_ERR_AXIS); + } Q->sign[n++] = sign(atoi(s)); while ( *s != '\0' && *s != ',' ) s++; -- cgit v1.2.3 From a00d13a1fdd869596bf2f13a39f12a0ab7961fb3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 11 Jan 2018 13:22:20 +0100 Subject: Fix breakage of previous commit --- src/PJ_axisswap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index 0d1683b4..725077a8 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -192,7 +192,7 @@ PJ *CONVERSION(axisswap,0) { /* read axes numbers and signs */ for ( s = order, n = 0; *s != '\0' && n < 4; ) { Q->axis[n] = abs(atoi(s))-1; - if (Q->axis[n] <= 0 || Q->axis[n] > 3) { + if (Q->axis[n] < 0 || Q->axis[n] > 3) { proj_log_error(P, "axisswap: invalid axis '%d'", Q->axis[n]); return pj_default_destructor(P, PJD_ERR_AXIS); } -- cgit v1.2.3 From 29916584ae00a16ef0974d419a89d6f12edecc12 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 11 Jan 2018 13:46:38 +0100 Subject: Fix breakage of previous commit (bis) --- src/PJ_axisswap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index 725077a8..f7e89c33 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -192,7 +192,7 @@ PJ *CONVERSION(axisswap,0) { /* read axes numbers and signs */ for ( s = order, n = 0; *s != '\0' && n < 4; ) { Q->axis[n] = abs(atoi(s))-1; - if (Q->axis[n] < 0 || Q->axis[n] > 3) { + if (Q->axis[n] > 3) { proj_log_error(P, "axisswap: invalid axis '%d'", Q->axis[n]); return pj_default_destructor(P, PJD_ERR_AXIS); } -- cgit v1.2.3 From 754f62987a8035497236e2fae24cc4e9d8d91937 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 11 Jan 2018 12:22:24 +0100 Subject: axisswap initialization: avoid switch case fallthrough case without break are really confusing, and should be avoided as much as possible IMHO. Also error out if a unrecognized character is provided as the axis value. --- src/PJ_axisswap.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index f7e89c33..d57834fc 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -211,19 +211,31 @@ PJ *CONVERSION(axisswap,0) { switch(P->axis[i]) { case 'w': Q->sign[i] = -1; + Q->axis[i] = 0; + break; case 'e': + Q->sign[i] = 1; Q->axis[i] = 0; break; case 's': Q->sign[i] = -1; + Q->axis[i] = 1; + break; case 'n': + Q->sign[i] = 1; Q->axis[i] = 1; break; case 'd': Q->sign[i] = -1; + Q->axis[i] = 2; + break; case 'u': + Q->sign[i] = 1; Q->axis[i] = 2; break; + default: + proj_log_error(P, "axisswap: unknown axis '%c'", P->axis[i]); + return pj_default_destructor(P, PJD_ERR_AXIS); } } n = 3; -- cgit v1.2.3 From fb69e67f8694fd61943cc9f9c2da75ec35ce9841 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 11 Jan 2018 16:34:47 +0100 Subject: Fail gracefully when invalid inverse operation is specified in cct. Similar to proj and cs2cs, cct now returns immediately when trying to do an inverse operation that is not possible, for example using proj=urm5 which doesn't have an inverse: $ cct.exe -I +proj=pipeline +step +proj=urm5 +n=0.5 Inverse operation not available --- src/cct.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cct.c b/src/cct.c index f315ae1a..7e48311a 100644 --- a/src/cct.c +++ b/src/cct.c @@ -217,9 +217,15 @@ int main(int argc, char **argv) { return 1; } - /* We have no API call for inverting an operation, so we brute force it. */ - if (direction==-1) + if (direction==-1) { + /* fail if an inverse operation is not available */ + if (!proj_pj_info(P).has_inverse) { + fprintf (stderr, "Inverse operation not available\n"); + return 1; + } + /* We have no API call for inverting an operation, so we brute force it. */ P->inverted = !(P->inverted); + } direction = 1; /* Allocate input buffer */ -- cgit v1.2.3 From b6c8e417d71dca9ac04f57660da2c94a0a85e8ff Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 11 Jan 2018 16:40:01 +0100 Subject: Set inv*-functions to zero on pipeline PJ's where an inverse does not exist. Some projections do not have an inverse mapping. If such a projection is used as a (forward) step in a pipeline we won't be able to perform an inverse operation using the pipeline. By setting the inv, inv3d and inv4d pointers to zero we signal to the caller that an inverse mapping is not available. --- src/PJ_pipeline.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index af9c5394..3dcc85d9 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -320,6 +320,7 @@ PJ *OPERATION(pipeline,0) { int i, nsteps = 0, argc; int i_pipeline = -1, i_first_step = -1, i_current_step; char **argv, **current_argv; + PJ *Q; P->fwd4d = pipeline_forward_4d; P->inv4d = pipeline_reverse_4d; @@ -429,14 +430,29 @@ PJ *OPERATION(pipeline,0) { /* Is this step inverted? */ for (j = 0; j < current_argc; j++) - if (0==strcmp("inv", current_argv[j])) + if (0==strcmp("inv", current_argv[j])) { next_step->inverted = 1; + } P->opaque->pipeline[i+1] = next_step; proj_log_trace (P, "Pipeline at [%p]: step at [%p] done", P, next_step); } + /* determine if an inverse operation is possible */ + for (i = 1; i <= nsteps; i++) { + Q = P->opaque->pipeline[i]; + if ( ( Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) || + ( Q->inv || Q->inv3d || Q->inv4d) ) { + continue; + } else { + P->inv = 0; + P->inv3d = 0; + P->inv4d = 0; + break; + } + } + proj_log_trace (P, "Pipeline: %d steps built. Determining i/o characteristics", nsteps); /* Determine forward input (= reverse output) data type */ -- cgit v1.2.3 From 9181fcfebde8a3969dc49100674f16286119059b Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 16 Jan 2018 13:16:18 +0100 Subject: Fix "double inversions" in pipelines, require a defined forward operation. "+proj=pipeline +inv +step +urm5 +n=0.5 +inv" now works as expected, returning the forward operation of urm5. In principle adding more +inv's should also work, resulting in the forward operation when an even number of +inv's are present, and the inverse when an odd number of +inv's are present. "+proj=pipeline +step +urm5 +n=0.5 +inv" fails at initialization since no forward operation can be performed. This is a new requirement, but aligns perfectly with the rest of the library since no operation without a forward method exists. --- src/PJ_pipeline.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index 3dcc85d9..befc8557 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -320,7 +320,6 @@ PJ *OPERATION(pipeline,0) { int i, nsteps = 0, argc; int i_pipeline = -1, i_first_step = -1, i_current_step; char **argv, **current_argv; - PJ *Q; P->fwd4d = pipeline_forward_4d; P->inv4d = pipeline_reverse_4d; @@ -431,7 +430,8 @@ PJ *OPERATION(pipeline,0) { /* Is this step inverted? */ for (j = 0; j < current_argc; j++) if (0==strcmp("inv", current_argv[j])) { - next_step->inverted = 1; + /* if +inv exists in both global and local args the forward operation should be used */ + next_step->inverted = next_step->inverted == 0 ? 1 : 0; } P->opaque->pipeline[i+1] = next_step; @@ -439,9 +439,21 @@ PJ *OPERATION(pipeline,0) { proj_log_trace (P, "Pipeline at [%p]: step at [%p] done", P, next_step); } + /* Require a forward path through the pipeline */ + for (i = 1; i <= nsteps; i++) { + PJ *Q = P->opaque->pipeline[i]; + if ( ( Q->inverted && (Q->inv || Q->inv3d || Q->fwd4d) ) || + (!Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) ) { + continue; + } else { + proj_log_error (P, "Pipeline: A forward operation couldn't be constructed"); + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); + } + } + /* determine if an inverse operation is possible */ for (i = 1; i <= nsteps; i++) { - Q = P->opaque->pipeline[i]; + PJ *Q = P->opaque->pipeline[i]; if ( ( Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) || ( Q->inv || Q->inv3d || Q->inv4d) ) { continue; -- cgit v1.2.3 From d0dbf48438f9e152314abf294467cb54f9ae0e70 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Mon, 22 Jan 2018 21:06:23 +0100 Subject: Handle ellipsoid parameters correctly when using +nadgrids=@null. Fixes #22. Make sure to not change ellipsoid parameters to WGS84 when applying the null grid. Coordinates will still refer to the input ellipsoid so we keep the original parameters which in turn will be used when the coordinates are transformated to/from cartesian/geocentric space. Adjusted regression test material in nad/proj_outIGNF.dist slightly to accomodate numerical differences at the mm level. The transformations in question are at best accurate to about 1m so this shouldn't change real world usage of these transformations. --- src/pj_transform.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pj_transform.c b/src/pj_transform.c index c72df2e7..6ad227cd 100644 --- a/src/pj_transform.c +++ b/src/pj_transform.c @@ -748,17 +748,30 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* -------------------------------------------------------------------- */ if( srcdefn->datum_type == PJD_GRIDSHIFT ) { + const char* srcnadgrids = pj_param(srcdefn->ctx, srcdefn->params,"snadgrids").s; + pj_apply_gridshift_2( srcdefn, 0, point_count, point_offset, x, y, z ); CHECK_RETURN(srcdefn); - src_a = SRS_WGS84_SEMIMAJOR; - src_es = SRS_WGS84_ESQUARED; + /* If the gridlist has either "@null" or "null" as its only */ + /* grid we don't change the ellipsoid parameters, since the */ + /* datum shift to WGS84 was not performed in practice. */ + if ( strcmp("@null", srcnadgrids) && strcmp("null", srcnadgrids) ) { + src_a = SRS_WGS84_SEMIMAJOR; + src_es = SRS_WGS84_ESQUARED; + } } if( dstdefn->datum_type == PJD_GRIDSHIFT ) { - dst_a = SRS_WGS84_SEMIMAJOR; - dst_es = SRS_WGS84_ESQUARED; + const char* dstnadgrids = pj_param(dstdefn->ctx, dstdefn->params,"snadgrids").s; + /* If the gridlist has either "@null" or "null" as its only */ + /* grid we don't change the ellipsoid parameters, since the */ + /* datum shift to WGS84 will not be performed. */ + if ( strcmp("@null", dstnadgrids) && strcmp("null", dstnadgrids) ) { + dst_a = SRS_WGS84_SEMIMAJOR; + dst_es = SRS_WGS84_ESQUARED; + } } /* ==================================================================== */ -- cgit v1.2.3 From aff1011a16372465ffeca05b0d7fcc8a9c2cf86e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 25 Jan 2018 15:51:37 +0100 Subject: Fix null pointer dereference in pj_datum_tranform() caused by d0dbf48438f9e152314abf294467cb54f9ae0e70 changes. Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=5649. Credit to OSS Fuzz --- src/pj_transform.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pj_transform.c b/src/pj_transform.c index 6ad227cd..fc0a3241 100644 --- a/src/pj_transform.c +++ b/src/pj_transform.c @@ -756,7 +756,8 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* If the gridlist has either "@null" or "null" as its only */ /* grid we don't change the ellipsoid parameters, since the */ /* datum shift to WGS84 was not performed in practice. */ - if ( strcmp("@null", srcnadgrids) && strcmp("null", srcnadgrids) ) { + if ( srcnadgrids != NULL && + strcmp("@null", srcnadgrids) && strcmp("null", srcnadgrids) ) { src_a = SRS_WGS84_SEMIMAJOR; src_es = SRS_WGS84_ESQUARED; } @@ -768,7 +769,8 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* If the gridlist has either "@null" or "null" as its only */ /* grid we don't change the ellipsoid parameters, since the */ /* datum shift to WGS84 will not be performed. */ - if ( strcmp("@null", dstnadgrids) && strcmp("null", dstnadgrids) ) { + if ( dstnadgrids != NULL && + strcmp("@null", dstnadgrids) && strcmp("null", dstnadgrids) ) { dst_a = SRS_WGS84_SEMIMAJOR; dst_es = SRS_WGS84_ESQUARED; } -- cgit v1.2.3 From b6c3d16dae36ed2dfd09e231c5171482553ff2d7 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Fri, 26 Jan 2018 15:40:35 +0100 Subject: Make sure to mark +proj param used so it shows up in the definition. Fixes #744. --- src/pj_init.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 862a6515..f25c0344 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -554,6 +554,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { curr = pj_param_exists (start, "proj"); if (0==curr) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); + curr->used = 1; name = curr->param; if (strlen (name) < 6) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); -- cgit v1.2.3 From e979bce36ccd2bc52cabc0b1192bc0f8d4ed6f33 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 31 Jan 2018 10:56:11 +0100 Subject: Add pj_has_inverse(). With the introduction of the "inverted" flag on PJ objects you can no longer rely on checking that the inv, inv3d and inv4d functions are available on said PJ object. The function is used internally a few places and otherwise exposed in proj_api.h to ensure that users of the old programming interface can safely check if an operation has an inverse. --- src/PJ_pipeline.c | 3 +-- src/pj_internal.c | 9 ++++++++- src/proj.def | 1 + src/proj_4D_api.c | 4 +--- src/proj_api.h | 1 + 5 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index befc8557..2f904ab1 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -454,8 +454,7 @@ PJ *OPERATION(pipeline,0) { /* determine if an inverse operation is possible */ for (i = 1; i <= nsteps; i++) { PJ *Q = P->opaque->pipeline[i]; - if ( ( Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) || - ( Q->inv || Q->inv3d || Q->inv4d) ) { + if ( pj_has_inverse(Q) ) { continue; } else { P->inv = 0; diff --git a/src/pj_internal.c b/src/pj_internal.c index deb0c054..6bb33d64 100644 --- a/src/pj_internal.c +++ b/src/pj_internal.c @@ -119,7 +119,14 @@ chained calls starting out with a call to its 3D interface. return proj_coord_error (); } - +/**************************************************************************************/ +int pj_has_inverse(PJ *P) { +/*************************************************************************************** +Check if a a PJ has an inverse. +***************************************************************************************/ + return ( (P->inverted && (P->fwd || P->fwd3d || P->fwd4d) ) || + ( P->inv || P->inv3d || P->inv4d) ); +} /* Move P to a new context - or to the default context if 0 is specified */ diff --git a/src/proj.def b/src/proj.def index f758477d..6ab0c007 100644 --- a/src/proj.def +++ b/src/proj.def @@ -154,3 +154,4 @@ EXPORTS pj_approx_2D_trans @138 pj_approx_3D_trans @139 + pj_has_inverse @140 diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 4a416084..2c6bac2d 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -696,9 +696,7 @@ PJ_PROJ_INFO proj_pj_info(PJ *P) { pj_strlcpy(info.definition, &def[1], sizeof(info.definition)); /* def includes a leading space */ pj_dealloc(def); - /* this does not take into account that a pipeline potentially does not */ - /* have an inverse. */ - info.has_inverse = (P->inv != 0 || P->inv3d != 0 || P->inv4d != 0); + info.has_inverse = pj_has_inverse(P); return info; } diff --git a/src/proj_api.h b/src/proj_api.h index 597a2589..40c40ad1 100644 --- a/src/proj_api.h +++ b/src/proj_api.h @@ -155,6 +155,7 @@ projPJ pj_init_ctx( projCtx, int, char ** ); projPJ pj_init_plus_ctx( projCtx, const char * ); char *pj_get_def(projPJ, int); projPJ pj_latlong_from_proj( projPJ ); +int pj_has_inverse(projPJ); void *pj_malloc(size_t); -- cgit v1.2.3 From 90968ff934a348b02f982080f8b7ccf17e037a46 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Fri, 5 Jan 2018 12:03:21 +0100 Subject: Introduce compatibility for cs2cs-style proj-strings into the 4D API. Parameters such as towgs84, nadgrids and geoidgrids was previously only handled by pj_transform(). This commit add a compatibility layer in proj_create() by calling the pj_cs2cs_emulation_setup() function. This function sets up a handful of predefined transformation objects on the PJ object that is being created. Each of these transformation objects are related to the cs2cs-style parameters we are trying to emulate in the 4D API. That is, if the +towgs84 parameters is used we create P->helmert with the parameters specified in +towgs84. Similarly for +axis, +nadgrids and +geoidgrids. When these transformation objects exists we use them in the prepare and finalize functions in pj_fwd/ pj_inv. If no cs2cs-style parametes are specified we skip those parts of the prepare and finalizing steps. Co-authored-by:Thomas Knudsen Co-authored-by:Kristian Evers --- src/PJ_helmert.c | 32 +++++++++++--- src/gie.c | 47 +++++++++++++------- src/pj_fwd.c | 115 ++++++++++++++++++++++++++++++++++++++++-------- src/pj_geocent.c | 2 + src/pj_init.c | 23 ---------- src/pj_inv.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-------- src/pj_malloc.c | 34 +++++++++++++++ src/proj.def | 2 + src/proj_4D_api.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++- src/proj_internal.h | 4 -- src/projects.h | 10 ++++- 11 files changed, 421 insertions(+), 90 deletions(-) (limited to 'src') 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) && diff --git a/src/gie.c b/src/gie.c index e0045ebd..b2673d13 100644 --- a/src/gie.c +++ b/src/gie.c @@ -387,6 +387,7 @@ Interpret 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; -- cgit v1.2.3 From b3354f70d3ca2c6b77d702f5ab73a0378819b703 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Fri, 26 Jan 2018 12:47:14 +0100 Subject: proj_roundtrip: simplify control logic --- src/proj_4D_api.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index fa24c430..6654522c 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -100,7 +100,7 @@ double proj_xyz_dist (XYZ a, XYZ b) { /* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coo) { int i; - PJ_COORD o, u, org; + PJ_COORD t, org; if (0==P) return HUGE_VAL; @@ -111,23 +111,23 @@ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coo) { } /* in the first half-step, we generate the output value */ - u = org = *coo; - o = *coo = proj_trans (P, direction, u); + org = *coo; + *coo = proj_trans (P, direction, org); + t = *coo; - /* now we take n-1 full steps */ - for (i = 0; i < n - 1; i++) { - u = proj_trans (P, -direction, o); - o = proj_trans (P, direction, u); - } + /* now we take n-1 full steps in inverse direction: We are */ + /* out of phase due to the half step already taken */ + for (i = 0; i < n - 1; i++) + t = proj_trans (P, direction, proj_trans (P, -direction, t) ); /* finally, we take the last half-step */ - u = proj_trans (P, -direction, o); + t = proj_trans (P, -direction, t); /* checking for angular *input* since we do a roundtrip, and end where we begin */ if (proj_angular_input (P, direction)) - return proj_lpz_dist (P, org.lpz, u.lpz); + return proj_lpz_dist (P, org.lpz, t.lpz); - return proj_xyz_dist (org.xyz, u.xyz); + return proj_xyz_dist (org.xyz, t.xyz); } -- cgit v1.2.3 From 52c20a2e095c63fab6e4158c92b8995d882cbe62 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 29 Jan 2018 20:47:44 +0100 Subject: Renamed PJ_IO_UNITS_RADIANS to PJ_IO_UNITS_ANGULAR --- src/PJ_axisswap.c | 4 ++-- src/PJ_cart.c | 2 +- src/PJ_geoc.c | 4 ++-- src/PJ_hgridshift.c | 4 ++-- src/PJ_latlong.c | 4 ++-- src/PJ_molodensky.c | 4 ++-- src/PJ_ob_tran.c | 2 +- src/PJ_vgridshift.c | 4 ++-- src/pj_fwd.c | 6 +++--- src/pj_geocent.c | 2 +- src/pj_inv.c | 8 ++++---- src/proj.c | 6 +++--- src/proj_4D_api.c | 4 ++-- src/proj_internal.h | 2 +- src/projects.h | 4 ++-- 15 files changed, 30 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c index d57834fc..44446d9c 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -274,8 +274,8 @@ PJ *CONVERSION(axisswap,0) { } if (pj_param(P->ctx, P->params, "tangularunits").i) { - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; } else { P->left = PJ_IO_UNITS_PROJECTED; P->right = PJ_IO_UNITS_PROJECTED; diff --git a/src/PJ_cart.c b/src/PJ_cart.c index 8e11c030..0746ec08 100644 --- a/src/PJ_cart.c +++ b/src/PJ_cart.c @@ -213,7 +213,7 @@ PJ *CONVERSION(cart,1) { P->inv3d = geodetic; P->fwd = cart_forward; P->inv = cart_reverse; - P->left = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; P->right = PJ_IO_UNITS_CARTESIAN; return P; } diff --git a/src/PJ_geoc.c b/src/PJ_geoc.c index 865b1089..20064f65 100644 --- a/src/PJ_geoc.c +++ b/src/PJ_geoc.c @@ -48,8 +48,8 @@ static PJ *CONVERSION(geoc, 1) { P->inv4d = inverse; P->fwd4d = forward; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; P->is_latlong = 1; return P; diff --git a/src/PJ_hgridshift.c b/src/PJ_hgridshift.c index 5c2b944d..54440822 100644 --- a/src/PJ_hgridshift.c +++ b/src/PJ_hgridshift.c @@ -54,8 +54,8 @@ PJ *TRANSFORMATION(hgridshift,0) { P->fwd = 0; P->inv = 0; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; if (0==pj_param(P->ctx, P->params, "tgrids").i) { proj_log_error(P, "hgridshift: +grids parameter missing."); diff --git a/src/PJ_latlong.c b/src/PJ_latlong.c index b1909954..1331d59a 100644 --- a/src/PJ_latlong.c +++ b/src/PJ_latlong.c @@ -98,8 +98,8 @@ static PJ *latlong_setup (PJ *P) { P->fwd3d = latlong_forward_3d; P->inv4d = latlong_inverse_4d; P->fwd4d = latlong_forward_4d; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; return P; } diff --git a/src/PJ_molodensky.c b/src/PJ_molodensky.c index 765e1d50..b537e802 100644 --- a/src/PJ_molodensky.c +++ b/src/PJ_molodensky.c @@ -281,8 +281,8 @@ PJ *TRANSFORMATION(molodensky,1) { P->fwd = forward_2d; P->inv = reverse_2d; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; /* read args */ if (pj_param(P->ctx, P->params, "tdx").i) diff --git a/src/PJ_ob_tran.c b/src/PJ_ob_tran.c index debb211c..c447ac08 100644 --- a/src/PJ_ob_tran.c +++ b/src/PJ_ob_tran.c @@ -232,7 +232,7 @@ PJ *PROJECTION(ob_tran) { /* Support some rather speculative test cases, where the rotated projection */ /* is actually latlong. We do not want scaling in that case... */ - if (Q->link->right==PJ_IO_UNITS_RADIANS) + if (Q->link->right==PJ_IO_UNITS_ANGULAR) P->right = PJ_IO_UNITS_PROJECTED; diff --git a/src/PJ_vgridshift.c b/src/PJ_vgridshift.c index 850734fc..bb8b4d4d 100644 --- a/src/PJ_vgridshift.c +++ b/src/PJ_vgridshift.c @@ -69,8 +69,8 @@ PJ *TRANSFORMATION(vgridshift,0) { P->fwd = 0; P->inv = 0; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; return P; } diff --git a/src/pj_fwd.c b/src/pj_fwd.c index 2293cf2e..0509858a 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -45,7 +45,7 @@ static PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; /* Check validity of angular input coordinates */ - if (INPUT_UNITS==PJ_IO_UNITS_RADIANS) { + if (INPUT_UNITS==PJ_IO_UNITS_ANGULAR) { double t; /* check for latitude or longitude over-range */ @@ -130,8 +130,8 @@ static PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { case PJ_IO_UNITS_WHATEVER: break; - case PJ_IO_UNITS_RADIANS: - if (INPUT_UNITS==PJ_IO_UNITS_RADIANS) + case PJ_IO_UNITS_ANGULAR: + if (INPUT_UNITS==PJ_IO_UNITS_ANGULAR) break; /* adjust longitude to central meridian */ diff --git a/src/pj_geocent.c b/src/pj_geocent.c index e676b735..6530b103 100644 --- a/src/pj_geocent.c +++ b/src/pj_geocent.c @@ -54,7 +54,7 @@ PJ *PROJECTION(geocent) { P->y0 = 0.0; P->inv = inverse; P->fwd = forward; - P->left = PJ_IO_UNITS_RADIANS; + P->left = PJ_IO_UNITS_ANGULAR; P->right = PJ_IO_UNITS_CARTESIAN; return P; diff --git a/src/pj_inv.c b/src/pj_inv.c index 02a2ca53..45e43820 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -49,7 +49,7 @@ static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { coo = proj_trans (P->axisswap, PJ_INV, coo); /* Check validity of angular input coordinates */ - if (INPUT_UNITS==PJ_IO_UNITS_RADIANS) { + if (INPUT_UNITS==PJ_IO_UNITS_ANGULAR) { double t; /* check for latitude or longitude over-range */ @@ -120,7 +120,7 @@ static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { coo.xyz.x *= P->ra; coo.xyz.y *= P->ra; return coo; - /* Silence some compiler warnings about PJ_IO_UNITS_RADIANS not handled */ + /* Silence some compiler warnings about PJ_IO_UNITS_ANGULAR not handled */ default: break; } @@ -137,9 +137,9 @@ static PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo) { return proj_coord_error (); } - if (OUTPUT_UNITS==PJ_IO_UNITS_RADIANS) { + if (OUTPUT_UNITS==PJ_IO_UNITS_ANGULAR) { - if (INPUT_UNITS!=PJ_IO_UNITS_RADIANS) { + if (INPUT_UNITS!=PJ_IO_UNITS_ANGULAR) { /* Distance from central meridian, taking system zero meridian into account */ coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; diff --git a/src/proj.c b/src/proj.c index 1d702ea4..1c86854e 100644 --- a/src/proj.c +++ b/src/proj.c @@ -152,12 +152,12 @@ static void process(FILE *fid) { } } else { /* x-y or decimal degree ascii output, scale if warranted by output units */ if (inverse) { - if (Proj->left == PJ_IO_UNITS_RADIANS) { + if (Proj->left == PJ_IO_UNITS_ANGULAR) { data.uv.v *= RAD_TO_DEG; data.uv.u *= RAD_TO_DEG; } } else { - if (Proj->right == PJ_IO_UNITS_RADIANS) { + if (Proj->right == PJ_IO_UNITS_ANGULAR) { data.uv.v *= RAD_TO_DEG; data.uv.u *= RAD_TO_DEG; } @@ -262,7 +262,7 @@ static void vprocess(FILE *fid) { } /* apply rad->deg scaling in case the output from a pipeline has degrees as units */ - if (!inverse && Proj->right == PJ_IO_UNITS_RADIANS) { + if (!inverse && Proj->right == PJ_IO_UNITS_ANGULAR) { dat_xy.x *= RAD_TO_DEG; dat_xy.y *= RAD_TO_DEG; } diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 6654522c..2b250398 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -52,8 +52,8 @@ int proj_angular_input (PJ *P, enum PJ_DIRECTION dir) { dir: {PJ_FWD, PJ_INV} ******************************************************************************/ if (PJ_FWD==dir) - return pj_left (P)==PJ_IO_UNITS_RADIANS; - return pj_right (P)==PJ_IO_UNITS_RADIANS; + return pj_left (P)==PJ_IO_UNITS_ANGULAR; + return pj_right (P)==PJ_IO_UNITS_ANGULAR; } /*****************************************************************************/ diff --git a/src/proj_internal.h b/src/proj_internal.h index 2c6cbee2..1b98c346 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -68,7 +68,7 @@ enum pj_io_units { PJ_IO_UNITS_CLASSIC = 1, /* Scaled meters (right), projected system */ PJ_IO_UNITS_PROJECTED = 2, /* Meters, projected system */ PJ_IO_UNITS_CARTESIAN = 3, /* Meters, 3D cartesian system */ - PJ_IO_UNITS_RADIANS = 4 /* Radians */ + PJ_IO_UNITS_ANGULAR = 4 /* Radians */ }; enum pj_io_units pj_left (PJ *P); enum pj_io_units pj_right (PJ *P); diff --git a/src/projects.h b/src/projects.h index aeaf09ae..e4bdca4f 100644 --- a/src/projects.h +++ b/src/projects.h @@ -196,7 +196,7 @@ enum pj_io_units { PJ_IO_UNITS_CLASSIC = 1, /* Scaled meters (right), projected system */ PJ_IO_UNITS_PROJECTED = 2, /* Meters, projected system */ PJ_IO_UNITS_CARTESIAN = 3, /* Meters, 3D cartesian system */ - PJ_IO_UNITS_RADIANS = 4 /* Radians */ + PJ_IO_UNITS_ANGULAR = 4 /* Radians */ }; #endif #ifndef PROJ_H @@ -623,7 +623,7 @@ C_NAMESPACE PJ *pj_##name (PJ *P) { \ P->destructor = pj_default_destructor; \ P->descr = des_##name; \ P->need_ellps = NEED_ELLPS; \ - P->left = PJ_IO_UNITS_RADIANS; \ + P->left = PJ_IO_UNITS_ANGULAR; \ P->right = PJ_IO_UNITS_CLASSIC; \ return P; \ } \ -- cgit v1.2.3 From 28d6b3a2cbf28f4820961f5a736ba9fe939da5c2 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 1 Feb 2018 14:14:15 +0100 Subject: Remove unnecessary functions and restructure proj.def. Closes #728 --- src/proj.def | 129 +++++++++++++++++++++++++--------------------------- src/proj_internal.h | 1 + src/projects.h | 1 - 3 files changed, 63 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/proj.def b/src/proj.def index 099a78a5..17300395 100644 --- a/src/proj.def +++ b/src/proj.def @@ -90,70 +90,65 @@ EXPORTS geod_polygon_clear @88 pj_find_file @89 - - proj_create @90 - proj_create_argv @91 - proj_create_crs_to_crs @92 - proj_destroy @93 - - proj_trans @94 - proj_trans_array @95 - proj_trans_generic @96 - proj_roundtrip @97 - - proj_coord @98 - proj_coord_error @99 - - proj_errno @100 - proj_errno_set @101 - proj_errno_reset @102 - proj_errno_restore @103 - proj_context_errno_set @104 - - proj_context_create @105 - proj_context_set @106 - proj_context_inherit @107 - proj_context_destroy @108 - - proj_lp_dist @109 - proj_lpz_dist @110 - proj_xy_dist @111 - proj_xyz_dist @112 - - proj_log_level @113 - proj_log_func @114 - proj_log_error @115 - proj_log_debug @116 - proj_log_trace @117 - - proj_info @118 - proj_pj_info @119 - proj_grid_info @120 - proj_init_info @121 - - proj_torad @122 - proj_todeg @123 - proj_geoc_lat @124 - proj_rtodms @125 - proj_dmstor @126 - - proj_factors @127 - - proj_list_operations @128 - proj_list_ellps @129 - proj_list_units @130 - proj_list_prime_meridians @131 - - proj_angular_input @132 - proj_angular_output @133 - - pj_ellipsoid @134 - pj_calc_ellipsoid_params @135 - pj_chomp @136 - pj_shrink @137 - - pj_approx_2D_trans @138 - pj_approx_3D_trans @139 - pj_has_inverse @140 - pj_param_exists @141 - + pj_chomp @90 + pj_shrink @91 + pj_approx_2D_trans @92 + pj_approx_3D_trans @93 + pj_has_inverse @94 + pj_param_exists @95 + + proj_create @96 + proj_create_argv @97 + proj_create_crs_to_crs @98 + proj_destroy @99 + + proj_trans @100 + proj_trans_array @101 + proj_trans_generic @102 + proj_roundtrip @103 + + proj_coord @104 + proj_coord_error @105 + + proj_errno @106 + proj_errno_set @107 + proj_errno_reset @108 + proj_errno_restore @109 + proj_context_errno_set @110 + + proj_context_create @111 + proj_context_set @112 + proj_context_inherit @113 + proj_context_destroy @114 + + proj_lp_dist @115 + proj_lpz_dist @116 + proj_xy_dist @117 + proj_xyz_dist @118 + + proj_log_level @119 + proj_log_func @120 + proj_log_error @121 + proj_log_debug @122 + proj_log_trace @123 + + proj_info @124 + proj_pj_info @125 + proj_grid_info @126 + proj_init_info @127 + + proj_torad @128 + proj_todeg @129 + proj_geoc_lat @130 + proj_rtodms @131 + proj_dmstor @132 + + proj_factors @133 + + proj_list_operations @134 + proj_list_ellps @135 + proj_list_units @136 + proj_list_prime_meridians @137 + + proj_angular_input @138 + proj_angular_output @139 diff --git a/src/proj_internal.h b/src/proj_internal.h index 1b98c346..3e0ecbe9 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -115,6 +115,7 @@ void proj_log_debug (PJ *P, const char *fmt, ...); void proj_log_trace (PJ *P, const char *fmt, ...); void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION log); +int pj_ellipsoid (PJ *); void pj_inherit_ellipsoid_def (const PJ *src, PJ *dst); void pj_erase_ellipsoid_def (PJ *P); int pj_calc_ellipsoid_params (PJ *P, double a, double es); diff --git a/src/projects.h b/src/projects.h index e4bdca4f..f22ae3a5 100644 --- a/src/projects.h +++ b/src/projects.h @@ -703,7 +703,6 @@ paralist *pj_mkparam(char *); paralist *pj_mkparam_ws (char *str); -int pj_ellipsoid (PJ *); int pj_ell_set(projCtx ctx, paralist *, double *, double *); int pj_datum_set(projCtx,paralist *, PJ *); int pj_prime_meridian_set(paralist *, PJ *); -- cgit v1.2.3 From 5c132e5be1a38a0ec018edcec86b3d889d6e33fa Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Thu, 1 Feb 2018 15:49:30 +0100 Subject: make local derivatives available in PJ_FACTOR --- src/proj.h | 3 +++ src/proj_4D_api.c | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/proj.h b/src/proj.h index 67c28b8d..0921eb51 100644 --- a/src/proj.h +++ b/src/proj.h @@ -167,6 +167,9 @@ struct P5_FACTORS { /* Common designation */ double tissot_semimajor; /* a */ double tissot_semiminor; /* b */ + + double dx_dlam, dx_dphi; + double dy_dlam, dy_dphi; }; typedef struct P5_FACTORS PJ_FACTORS; diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 2b250398..c91b0786 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -940,11 +940,11 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { Characteristics include meridian, parallel and areal scales, angular distortion, meridian/parallel, meridian convergence and scale error. - returns PJ_FACTORS. If unsuccessfull error number is set and the returned - struct contains NULL data. + returns PJ_FACTORS. If unsuccessfull, error number is set and the + struct returned contains NULL data. ******************************************************************************/ - PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0}; + PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0, 0,0,0,0}; struct FACTORS f; if (0==P) @@ -964,6 +964,12 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { factors.tissot_semimajor = f.a; factors.tissot_semiminor = f.b; + /* Raw derivatives, for completeness's sake */ + factors.dx_dlam = f.der.x_l; + factors.dx_dphi = f.der.x_p; + factors.dy_dlam = f.der.y_l; + factors.dy_dphi = f.der.y_p; + return factors; } -- cgit v1.2.3 From dd9cd3f821295f4095d3a261bfd9be26065e60b7 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Thu, 1 Feb 2018 16:52:10 +0100 Subject: expand proj_geoc_lat to proj_geocentric_latitude (#751) --- src/PJ_geoc.c | 4 ++-- src/pj_factors.c | 2 +- src/pj_fwd.c | 4 ++-- src/pj_inv.c | 4 ++-- src/proj.def | 2 +- src/proj.h | 2 +- src/proj_4D_api.c | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/PJ_geoc.c b/src/PJ_geoc.c index 20064f65..038da600 100644 --- a/src/PJ_geoc.c +++ b/src/PJ_geoc.c @@ -35,12 +35,12 @@ PROJ_HEAD(geoc, "Geocentric Latitude"); /* Geographical to geocentric */ static PJ_COORD forward(PJ_COORD coo, PJ *P) { - return proj_geoc_lat (P, PJ_FWD, coo); + return proj_geocentric_latitude (P, PJ_FWD, coo); } /* Geocentric to geographical */ static PJ_COORD inverse(PJ_COORD coo, PJ *P) { - return proj_geoc_lat (P, PJ_INV, coo); + return proj_geocentric_latitude (P, PJ_INV, coo); } diff --git a/src/pj_factors.c b/src/pj_factors.c index 6ba993b9..17b39c10 100644 --- a/src/pj_factors.c +++ b/src/pj_factors.c @@ -47,7 +47,7 @@ int pj_factors(LP lp, const PJ *P, double h, struct FACTORS *fac) { /* If input latitudes are geocentric, convert to geographic */ if (P->geoc) - lp = proj_geoc_lat (P, PJ_INV, coo).lp; + lp = proj_geocentric_latitude (P, PJ_INV, coo).lp; /* If latitude + one step overshoots the pole, move it slightly inside, */ /* so the numerical derivative still exists */ diff --git a/src/pj_fwd.c b/src/pj_fwd.c index 0509858a..66b86aab 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -63,7 +63,7 @@ static PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { /* If input latitude is geocentrical, convert to geographical */ if (P->geoc) - coo = proj_geoc_lat (P, PJ_INV, coo); + coo = proj_geocentric_latitude (P, PJ_INV, coo); /* Ensure longitude is in the -pi:pi range */ if (0==P->over) @@ -150,7 +150,7 @@ static PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { /* If input latitude was geocentrical, convert back to geocentrical */ if (P->geoc) - coo = proj_geoc_lat (P, PJ_FWD, coo); + coo = proj_geocentric_latitude (P, PJ_FWD, coo); /* Distance from central meridian, taking system zero meridian into account */ diff --git a/src/pj_inv.c b/src/pj_inv.c index 45e43820..5e44eb34 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -67,7 +67,7 @@ static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { /* If input latitude is geocentrical, convert to geographical */ if (P->geoc) - coo = proj_geoc_lat (P, PJ_INV, coo); + coo = proj_geocentric_latitude (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; @@ -160,7 +160,7 @@ static PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo) { /* If input latitude was geocentrical, convert back to geocentrical */ if (P->geoc) - coo = proj_geoc_lat (P, PJ_FWD, coo); + coo = proj_geocentric_latitude (P, PJ_FWD, coo); } return coo; diff --git a/src/proj.def b/src/proj.def index 17300395..f2936edb 100644 --- a/src/proj.def +++ b/src/proj.def @@ -139,7 +139,7 @@ EXPORTS proj_torad @128 proj_todeg @129 - proj_geoc_lat @130 + proj_geocentric_latitude @130 proj_rtodms @131 proj_dmstor @132 diff --git a/src/proj.h b/src/proj.h index 0921eb51..c3165cfe 100644 --- a/src/proj.h +++ b/src/proj.h @@ -375,7 +375,7 @@ double proj_torad (double angle_in_degrees); double proj_todeg (double angle_in_radians); /* Geographical to geocentric latitude - another of the "simple, but useful" */ -PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo); +PJ_COORD proj_geocentric_latitude (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo); double proj_dmstor(const char *is, char **rs); char* proj_rtodms(char *s, double r, int pos, int neg); diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index c91b0786..af4bae45 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -342,7 +342,7 @@ size_t proj_trans_generic ( /*************************************************************************************/ -PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { +PJ_COORD proj_geocentric_latitude (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { /************************************************************************************** Convert geographical latitude to geocentric (or the other way round if direction = PJ_INV) -- cgit v1.2.3 From 56600aadd345a984c1f5a1f42b7932e67c9bee03 Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Thu, 1 Feb 2018 20:09:08 +0100 Subject: Remove Windows CE cruft (wince/msvc80) Closes #582 --- src/pj_open_lib.c | 5 ---- src/pj_strtod.c | 2 +- src/proj_config.h.wince | 66 ------------------------------------------------- src/projects.h | 12 ++------- 4 files changed, 3 insertions(+), 82 deletions(-) delete mode 100644 src/proj_config.h.wince (limited to 'src') diff --git a/src/pj_open_lib.c b/src/pj_open_lib.c index 4eaefba1..054853c6 100644 --- a/src/pj_open_lib.c +++ b/src/pj_open_lib.c @@ -118,8 +118,6 @@ pj_open_lib_ex(projCtx ctx, const char *name, const char *mode, static const char dir_chars[] = "/"; #endif -#ifndef _WIN32_WCE - if( out_full_filename != NULL && out_full_filename_size > 0 ) out_full_filename[0] = '\0'; @@ -205,9 +203,6 @@ pj_open_lib_ex(projCtx ctx, const char *name, const char *mode, fid == NULL ? "failed" : "succeeded" ); return(fid); -#else - return NULL; -#endif /* _WIN32_WCE */ } /************************************************************************/ diff --git a/src/pj_strtod.c b/src/pj_strtod.c index 4c03a661..90d0b2e5 100644 --- a/src/pj_strtod.c +++ b/src/pj_strtod.c @@ -75,7 +75,7 @@ double pj_atof( const char* nptr ) static char* pj_replace_point_by_locale_point(const char* pszNumber, char point, char* pszWorkBuffer) { -#if !defined(HAVE_LOCALECONV) || defined(_WIN32_WCE) +#if !defined(HAVE_LOCALECONV) #if defined(_MSC_VER) /* Visual C++ */ #pragma message("localeconv not available") diff --git a/src/proj_config.h.wince b/src/proj_config.h.wince deleted file mode 100644 index 9e2c8a67..00000000 --- a/src/proj_config.h.wince +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Windows CE platform config. - */ -#ifndef _WIN32_WCE -# error This version of proj_config.h header is dedicated for Windows CE platform! -#endif - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `m' library (-lm). */ -#undef HAVE_LIBM - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Enabled for Java/JNI Support */ -#undef JNI_ENABLED - -/* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Version number of package */ -#undef VERSION diff --git a/src/projects.h b/src/projects.h index f22ae3a5..41f4aa3c 100644 --- a/src/projects.h +++ b/src/projects.h @@ -97,14 +97,6 @@ typedef long pj_int32; extern double hypot(double, double); #endif -#ifdef _WIN32_WCE -# include -# include -# define rewind wceex_rewind -# define getenv wceex_getenv -# define hypot _hypot -#endif - /* If we still haven't got M_PI*, we rely on our own defines. * For example, this is necessary when compiling with gcc and * the -ansi flag. @@ -136,11 +128,11 @@ extern double hypot(double, double); #endif /* Use WIN32 as a standard windows 32 bit declaration */ -#if defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) +#if defined(_WIN32) && !defined(WIN32) # define WIN32 #endif -#if defined(_WINDOWS) && !defined(WIN32) && !defined(_WIN32_WCE) +#if defined(_WINDOWS) && !defined(WIN32) # define WIN32 #endif -- cgit v1.2.3 From 4ffec0f7a70f344e34b9863ca8e07b88178f4241 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 10 Jan 2018 11:41:29 +0100 Subject: Bump various version numbers in preparation for v. 5.0.0 --- src/Makefile.am | 2 +- src/pj_release.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index e7e53394..aebfc22b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,7 +40,7 @@ geodtest_LDADD = libproj.la lib_LTLIBRARIES = libproj.la -libproj_la_LDFLAGS = -no-undefined -version-info 12:0:0 +libproj_la_LDFLAGS = -no-undefined -version-info 13:0:1 libproj_la_SOURCES = \ pj_list.h proj_internal.h\ diff --git a/src/pj_release.c b/src/pj_release.c index 41d02a8b..fdd66f90 100644 --- a/src/pj_release.c +++ b/src/pj_release.c @@ -2,7 +2,7 @@ #include -char const pj_release[]="Rel. 5.0.0, development version"; +char const pj_release[]="Rel. 5.0.0, 15 february 2018"; const char *pj_get_release() -- cgit v1.2.3 From c0099e5ad4515e4ee1d672328731b004071489f4 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Thu, 1 Feb 2018 20:01:52 +0100 Subject: Make sure to mark parameters used when found with pj_param_exists. Closes #752 --- src/pj_init.c | 1 - src/pj_param.c | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 51a742c7..0acc6c71 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -554,7 +554,6 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { curr = pj_param_exists (start, "proj"); if (0==curr) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); - curr->used = 1; name = curr->param; if (strlen (name) < 6) return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); diff --git a/src/pj_param.c b/src/pj_param.c index 72175ed9..133f3ea6 100644 --- a/src/pj_param.c +++ b/src/pj_param.c @@ -75,8 +75,10 @@ paralist *pj_param_exists (paralist *list, const char *parameter) { return 0; for (next = list; next; next = next->next) { - if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) + if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) { + next->used = 1; return next; + } if (0==strcmp (parameter, "step")) return 0; } -- cgit v1.2.3 From 85cf25a6c293c3805fb7133f4c583dd36e7e57c5 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Fri, 2 Feb 2018 10:33:40 +0100 Subject: PJ_omerc: Guard against asin calls with arg numerically greater than 1 --- src/PJ_omerc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/PJ_omerc.c b/src/PJ_omerc.c index 22f0daca..eca5b241 100644 --- a/src/PJ_omerc.c +++ b/src/PJ_omerc.c @@ -177,12 +177,12 @@ PJ *PROJECTION(omerc) { } if (alp || gam) { if (alp) { - gamma0 = asin(sin(alpha_c) / D); + gamma0 = aasin(P->ctx, sin(alpha_c) / D); if (!gam) gamma = alpha_c; } else - alpha_c = asin(D*sin(gamma0 = gamma)); - P->lam0 = lamc - asin(.5 * (F - 1. / F) * + alpha_c = aasin(P->ctx, D*sin(gamma0 = gamma)); + P->lam0 = lamc - aasin(P->ctx, .5 * (F - 1. / F) * tan(gamma0)) / Q->B; } else { H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B); @@ -199,7 +199,7 @@ PJ *PROJECTION(omerc) { J * tan(.5 * Q->B * (lam1 - lam2)) / p) / Q->B); gamma0 = atan(2. * sin(Q->B * adjlon(lam1 - P->lam0)) / (F - 1. / F)); - gamma = alpha_c = asin(D * sin(gamma0)); + gamma = alpha_c = aasin(P->ctx, D * sin(gamma0)); } Q->singam = sin(gamma0); Q->cosgam = cos(gamma0); -- cgit v1.2.3 From d14bf09d94be40e3e2ed2be5b0b55c9d885f1513 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Fri, 2 Feb 2018 14:58:00 +0100 Subject: Avoid buffer overflow - OSSFuzz issue 5903 --- src/proj_4D_api.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index af4bae45..0d50407f 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -402,7 +402,6 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. **************************************************************************************/ PJ *Q; paralist *p; - char def[1000]; if (0==P) return 0; @@ -415,8 +414,12 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. /* 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); @@ -426,8 +429,12 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. 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); @@ -437,8 +444,12 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. 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); @@ -447,6 +458,7 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. /* 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); @@ -455,12 +467,15 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. 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; + + def = malloc (100+n); + if (0==def) + return 0; sprintf (def, "break_cs2cs_recursion proj=helmert %s", s); Q = proj_create (P->ctx, def); + free (def); if (0==Q) return 0; P->helmert = skip_prep_fin(Q); @@ -471,7 +486,7 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. /* 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"; + char def[100]; sprintf (def, "break_cs2cs_recursion proj=cart"); Q = proj_create (P->ctx, def); if (0==Q) @@ -479,7 +494,7 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. pj_inherit_ellipsoid_def(P, Q); P->cart = skip_prep_fin(Q); - sprintf (def, "break_cs2cs_recursion proj=cart %s", wgs84); + sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); Q = proj_create (P->ctx, def); if (0==Q) return 0; -- cgit v1.2.3 From cc50f3dfcfba4faeb0ee44efda6e1001a885a93b Mon Sep 17 00:00:00 2001 From: Bas Couwenberg Date: Fri, 2 Feb 2018 19:59:00 +0100 Subject: Fix SOVERSION for Automake build. Fixes: #763 --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index aebfc22b..f94e5f26 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,7 +40,7 @@ geodtest_LDADD = libproj.la lib_LTLIBRARIES = libproj.la -libproj_la_LDFLAGS = -no-undefined -version-info 13:0:1 +libproj_la_LDFLAGS = -no-undefined -version-info 13:0:0 libproj_la_SOURCES = \ pj_list.h proj_internal.h\ -- cgit v1.2.3 From 0ca9e9ff582e9ed2243c1991326b172880d94f35 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sun, 4 Feb 2018 15:49:48 +0100 Subject: Add --version option to gie and cct. Additionally correct a spelling error in optargpm.h and remove two lines of leftover cruft from gie.c --- src/cct.c | 7 ++++++- src/gie.c | 12 ++++++++---- src/optargpm.h | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cct.c b/src/cct.c index 7e48311a..2ce478bd 100644 --- a/src/cct.c +++ b/src/cct.c @@ -157,7 +157,7 @@ int main(int argc, char **argv) { int nfields = 4, direction = 1, verbose; double fixed_z = HUGE_VAL, fixed_time = HUGE_VAL; int columns_xyzt[] = {1, 2, 3, 4}; - const char *longflags[] = {"v=verbose", "h=help", "I=inverse", 0}; + const char *longflags[] = {"v=verbose", "h=help", "I=inverse", "version", 0}; const char *longkeys[] = {"o=output", "c=columns", "z=height", "t=time", 0}; o = opt_parse (argc, argv, "hvI", "cozt", longflags, longkeys); @@ -173,6 +173,11 @@ int main(int argc, char **argv) { direction = opt_given (o, "I")? -1: 1; verbose = opt_given (o, "v"); + if (opt_given (o, "version")) { + fprintf (stdout, "%s: %s\n", o->progname, pj_get_release ()); + return 0; + } + if (opt_given (o, "o")) fout = fopen (opt_arg (o, "output"), "wt"); if (0==fout) { diff --git a/src/gie.c b/src/gie.c index b2673d13..ee5bae44 100644 --- a/src/gie.c +++ b/src/gie.c @@ -133,8 +133,6 @@ typedef struct ffio { size_t level; } ffio; -FILE *test = 0; - static int get_inp (ffio *F); static int skip_to_next_tag (ffio *F); static int step_into_gie_block (ffio *F); @@ -231,7 +229,7 @@ static const char usage[] = { int main (int argc, char **argv) { int i; - const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", 0}; + const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", "version", 0}; const char *longkeys[] = {"o=output", 0}; OPTARGS *o; @@ -240,7 +238,6 @@ int main (int argc, char **argv) { T.verbosity = 1; T.tolerance = 5e-4; - test = fopen ("test.c", "wt"); o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys); if (0==o) return 0; @@ -250,6 +247,13 @@ int main (int argc, char **argv) { return 0; } + + if (opt_given (o, "version")) { + fprintf (stdout, "%s: %s\n", o->progname, pj_get_release ()); + return 0; + } + + if (opt_given (o, "l")) return list_err_codes (); diff --git a/src/optargpm.h b/src/optargpm.h index d464b5a9..8a9dd0c7 100644 --- a/src/optargpm.h +++ b/src/optargpm.h @@ -84,7 +84,7 @@ opt_eof_handler (o): Usage is probably easiest understood by a brief textbook style example: -Consider a simple program taking the conventinal "-v, -h, -o" options +Consider a simple program taking the conventional "-v, -h, -o" options indicating "verbose output", "help please", and "output file specification", respectively. -- cgit v1.2.3 From 0e7cb70f167858ec098343e8a991fcb41435880a Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 6 Feb 2018 16:00:01 +0100 Subject: Let gie skip tests when specific errors are returned. Introducing a new keyword 'skiperror' that when given a error code like pjd_err_failed_to_load_grid will skip tests if that error is returned when creating the operation. This is useful for grids that can't be located. --- src/gie.c | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index ee5bae44..6aa185f8 100644 --- a/src/gie.c +++ b/src/gie.c @@ -146,7 +146,7 @@ static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_si static const char *gie_tags[] = { "", "operation", "accept", "expect", "roundtrip", "banner", "verbose", - "direction", "tolerance", "builtins", "echo", "" + "direction", "tolerance", "ignore", "builtins", "echo", "" }; static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0]; @@ -178,12 +178,13 @@ typedef struct { PJ_DIRECTION dir; int verbosity; int op_id; - int op_ok, op_ko; - int total_ok, total_ko; - int grand_ok, grand_ko; + int op_ok, op_ko, op_skip; + int total_ok, total_ko, total_skip; + int grand_ok, grand_ko, grand_skip; size_t operation_lineno; size_t dimensions_given, dimensions_given_at_last_accept; double tolerance; + int ignore; const char *curr_file; FILE *fout; } gie_ctx; @@ -237,6 +238,7 @@ int main (int argc, char **argv) { T.dir = PJ_FWD; T.verbosity = 1; T.tolerance = 5e-4; + T.ignore = 5555; /* Error code that will not be issued by proj_create() */ o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys); if (0==o) @@ -294,7 +296,8 @@ int main (int argc, char **argv) { if (T.verbosity > 0) { if (o->fargc > 1) - fprintf (T.fout, "%sGrand total: %d. Success: %d, Failure: %d\n", delim, T.grand_ok+T.grand_ko, T.grand_ok, T.grand_ko); + fprintf (T.fout, "%sGrand total: %d. Success: %d, Skipped: %d, Failure: %d\n", + delim, T.grand_ok+T.grand_ko+T.grand_skip, T.grand_ok, T.grand_skip, T.grand_ko); fprintf (T.fout, "%s", delim); } else @@ -315,6 +318,12 @@ static int another_failure (void) { return 0; } +static int another_skip (void) { + T.op_skip++; + T.total_skip++; + return 0; +} + static int another_success (void) { T.op_ok++; T.total_ok++; @@ -328,6 +337,7 @@ static int process_file (const char *fname) { F->lineno = F->next_lineno = F->level = 0; T.op_ok = T.total_ok = 0; T.op_ko = T.total_ko = 0; + T.op_skip = T.total_skip = 0; f = fopen (fname, "rt"); if (0==f) { @@ -351,10 +361,12 @@ static int process_file (const char *fname) { fclose (f); F->lineno = F->next_lineno = 0; - T.grand_ok += T.total_ok; - T.grand_ko += T.total_ko; + T.grand_ok += T.total_ok; + T.grand_ko += T.total_ko; + T.grand_skip += T.grand_skip; if (T.verbosity > 0) - fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests %s\n", delim, T.total_ok, T.total_ko, T.total_ko? "FAILED!": "failed."); + fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests skipped, %2d tests %s\n", + delim, T.total_ok, T.total_skip, T.total_ko, T.total_ko? "FAILED!": "failed."); if (F->level==0) return errmsg (-3, "File '%s':Missing '' cmnd - bye!\n", fname); @@ -441,6 +453,10 @@ static int tolerance (const char *args) { return 0; } +static int ignore (const char *args) { + T.ignore = errno_from_err_const (column (args, 1)); + return 0; +} static int direction (const char *args) { const char *endp = args; @@ -467,7 +483,8 @@ static int direction (const char *args) { static void finish_previous_operation (const char *args) { if (T.verbosity > 1 && T.op_id > 1 && T.op_ok+T.op_ko) - fprintf (T.fout, "%s %d tests succeeded, %d tests %s\n", delim, T.op_ok, T.op_ko, T.op_ko? "FAILED!": "failed."); + fprintf (T.fout, "%s %d tests succeeded, %d tests skipped, %d tests %s\n", + delim, T.op_ok, T.op_skip, T.op_ko, T.op_ko? "FAILED!": "failed."); (void) args; } @@ -494,9 +511,11 @@ either a conversion or a transformation) T.op_ok = 0; T.op_ko = 0; + T.op_skip = 0; direction ("forward"); tolerance ("0.5 mm"); + ignore ("pjd_err_dont_skip"); proj_errno_reset (T.P); @@ -533,6 +552,7 @@ using the "builtins" command verb. } T.op_ok = 0; T.op_ko = 0; + T.op_skip = 0; i = pj_unitconvert_selftest (); if (i!=0) { fprintf (T.fout, "pj_unitconvert_selftest fails with %d\n", i); @@ -637,8 +657,12 @@ back/forward transformation pairs. char *endp; PJ_COORD coo; - if (0==T.P) + if (0==T.P) { + if (T.ignore == proj_errno(T.P)) + return another_skip(); + return another_failure (); + } ans = proj_strtod (args, &endp); ntrips = (int) (endp==args? 100: fabs(ans)); @@ -745,12 +769,16 @@ Tell GIE what to expect, when transforming the ACCEPTed input expect_failure_with_errno = errno_from_err_const (column (args, 3)); } + if (T.ignore==proj_errno(T.P)) + return another_skip (); + if (0==T.P) { /* If we expect failure, and fail, then it's a success... */ if (expect_failure) { /* Failed to fail correctly? */ if (expect_failure_with_errno && proj_errno (T.P)!=expect_failure_with_errno) return expect_failure_with_errno_message (expect_failure_with_errno, proj_errno(T.P)); + return another_success (); } @@ -881,6 +909,7 @@ static int dispatch (const char *cmnd, const char *args) { if (0==strcmp (cmnd, "verbose")) return verbose (args); if (0==strcmp (cmnd, "direction")) return direction (args); if (0==strcmp (cmnd, "tolerance")) return tolerance (args); + if (0==strcmp (cmnd, "ignore")) return ignore (args); if (0==strcmp (cmnd, "builtins")) return builtins (args); if (0==strcmp (cmnd, "echo")) return echo (args); @@ -950,6 +979,7 @@ static const struct errno_vs_err_const lookup[] = { {"pjd_err_ellipsoidal_unsupported" , -56}, {"pjd_err_too_many_inits" , -57}, {"pjd_err_invalid_arg" , -58}, + {"pjd_err_dont_skip" , 5555}, {"pjd_err_unknown" , 9999}, {"pjd_err_enomem" , ENOMEM}, }; -- cgit v1.2.3 From 27200f2f7689cb77546efb2e3ba0e795c46fd45f Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 6 Feb 2018 20:52:31 +0100 Subject: Make pj_free return with the correct errno. Error codes can't be specified directly in pj_free. With this change the error number in proj_errno() is passed on to P->destructor to ensure that we communicate the correct reason why the PJ object is being freed. This was a problem in the cs2cs_emulation setup when auxillary operations failed to create. The error was not propagated properly when destroying the parent object. --- src/pj_malloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pj_malloc.c b/src/pj_malloc.c index cfb7f658..73a60599 100644 --- a/src/pj_malloc.c +++ b/src/pj_malloc.c @@ -182,7 +182,7 @@ void pj_free(PJ *P) { /* 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); + P->destructor (P, proj_errno(P)); } -- cgit v1.2.3 From b2ce811f40d14e4659869597200388a13ccffeaf Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 6 Feb 2018 20:59:33 +0100 Subject: Use a grid in API selftest that we can always rely on being present. --- src/gie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 6aa185f8..40820e59 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1739,9 +1739,9 @@ static int pj_cart_selftest (void) { proj_destroy(P); /* proj_grid_info() */ - grid_info = proj_grid_info("egm96_15.gtx"); + grid_info = proj_grid_info("null"); if ( strlen(grid_info.filename) == 0 ) return 64; - if ( strcmp(grid_info.gridname, "egm96_15.gtx") ) return 65; + if ( strcmp(grid_info.gridname, "null") ) return 65; grid_info = proj_grid_info("nonexistinggrid"); if ( strlen(grid_info.filename) > 0 ) return 66; -- cgit v1.2.3 From 4f00f09f1c306e87dfea3a5bc49dcaae72280e00 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Wed, 7 Feb 2018 12:15:04 +0100 Subject: Shrink PJ_XXX_INFO structs, but keep same syntax. (#775) * Shrink PJ_XXX_INFO structs, but keep same syntax. A number of the fixed length strings in the INFO structs are simply reflections of material that already exists as static strings at a number of places in the library (or in the case of PJ_INFO, really *should* exist, and now is implemented). This PR replaces these cases of constant length strings with const char pointers. The usage syntax is unchanged, and so is the nice property of having the return value allocated on the stack, and hence not requiring explicit memory management by the caller. proj_info now only does setup once - and the searchpath entry of PJ_INFO is not arbitrarily truncated at 512 bytes. Repeated calls simply returns a copy of already prepared material. The id, description and definition entries of PJ_PROJ_INFO are now also guaranteed to hold the entire text of the corresponding static string, by being represented by a const char pointer to that actual static string. PJ_GRID_INFO and PJ_INIT_INFO (i.e. the two smallest INFO structs) are unchanged. * Eliminate pj_strlcpy - not needed anymore: Remining calls could safely be replaced by strncpy. * Extend PROJ_INFO with paths from pj_set_searchpath. NOTE: Need to call pj_set_searchpath before first call to proj_info Huge thanks to Kristian Evers and Even Rouault for comments, debugging and advice. --- src/gie.c | 6 +- src/pj_internal.c | 54 +----------- src/pj_malloc.c | 1 + src/proj.h | 16 ++-- src/proj_4D_api.c | 238 +++++++++++++++++++++++++++++++--------------------- src/proj_internal.h | 2 - src/projects.h | 1 + 7 files changed, 160 insertions(+), 158 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 40820e59..9931b2ef 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1492,7 +1492,7 @@ static int pj_cart_selftest (void) { size_t n, sz; double dist, h, t; char *args[3] = {"proj=utm", "zone=32", "ellps=GRS80"}; - const char *arg = {"+proj=utm +zone=32 +ellps=GRS80"}; + char arg[50] = {"+proj=utm; +zone=32; +ellps=GRS80"}; char buf[40]; /* An utm projection on the GRS80 ellipsoid */ @@ -1723,7 +1723,8 @@ static int pj_cart_selftest (void) { if (strcmp(info.version, tmpstr)) return 55; } if (info.release[0] == '\0') return 56; - if (info.searchpath[0] == '\0') return 57; + if (getenv ("HOME") || getenv ("PROJ_LIB")) + if (info.searchpath[0] == '\0') return 57; /* proj_pj_info() */ P = proj_create(PJ_DEFAULT_CTX, "+proj=august"); /* august has no inverse */ @@ -1733,6 +1734,7 @@ static int pj_cart_selftest (void) { P = proj_create(PJ_DEFAULT_CTX, arg); pj_info = proj_pj_info(P); if ( !pj_info.has_inverse ) { proj_destroy(P); return 61; } + pj_shrink (arg); if ( strcmp(pj_info.definition, arg) ) { proj_destroy(P); return 62; } if ( strcmp(pj_info.id, "utm") ) { proj_destroy(P); return 63; } diff --git a/src/pj_internal.c b/src/pj_internal.c index 6bb33d64..4dbcfbd4 100644 --- a/src/pj_internal.c +++ b/src/pj_internal.c @@ -148,56 +148,6 @@ void proj_context_inherit (PJ *parent, PJ *child) { -/**************************************************************************************/ -size_t pj_strlcpy(char *dst, const char *src, size_t dsize) { -/*************************************************************************************** - Copy src to string dst of size siz. At most siz-1 characters - will be copied. Always NUL terminates (unless siz == 0). - Returns strlen(src); if retval >= siz, truncation occurred. - - * - * Copyright (c) 1998, 2015 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - - Source: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c - -***************************************************************************************/ - const char *osrc = src; - size_t nleft = dsize; - - /* Copy as many bytes as will fit. */ - if (nleft != 0) { - while (--nleft != 0) { - if ((*dst++ = *src++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src. */ - if (nleft == 0) { - if (dsize != 0) - *dst = '\0'; /* NUL-terminate dst */ - while (*src++) - ; - } - - return(src - osrc - 1); /* count does not include NUL */ -} - - - /*****************************************************************************/ char *pj_chomp (char *c) { /****************************************************************************** @@ -261,7 +211,9 @@ consuming their surrounding whitespace. /* Eliminate prefix '+', only if preceeded by whitespace */ /* (i.e. keep it in 1.23e+08) */ - if ((i > 0) && ('+'==c[j]) && isspace (c[i])) + if ((i > 0) && ('+'==c[j]) && ws) + c[j] = ' '; + if ((i==0) && ('+'==c[j])) c[j] = ' '; if (isspace (c[j]) || ';'==c[j]) { diff --git a/src/pj_malloc.c b/src/pj_malloc.c index 73a60599..127e76ee 100644 --- a/src/pj_malloc.c +++ b/src/pj_malloc.c @@ -221,6 +221,7 @@ void *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ /* free parameter list elements */ pj_dealloc_params (pj_get_ctx(P), P->params, errlev); + pj_dealloc (P->def_full); /* free the cs2cs emulation elements */ pj_free (P->axisswap); diff --git a/src/proj.h b/src/proj.h index c3165cfe..73629490 100644 --- a/src/proj.h +++ b/src/proj.h @@ -239,20 +239,22 @@ union PJ_COORD { struct PJ_INFO { - char release[64]; /* Release info. Version + date */ - char version[64]; /* Full version number */ int major; /* Major release number */ int minor; /* Minor release number */ int patch; /* Patch level */ - char searchpath[512]; /* Paths where init and grid files are */ + const char *release; /* Release info. Version + date */ + const char *version; /* Full version number */ + const char *searchpath; /* Paths where init and grid files are */ /* looked for. Paths are separated by */ /* semi-colons. */ + const char * const *paths; + size_t path_count; }; struct PJ_PROJ_INFO { - char id[16]; /* Name of the projection in question */ - char description[128]; /* Description of the projection */ - char definition[512]; /* Projection definition */ + const char *id; /* Name of the projection in question */ + const char *description; /* Description of the projection */ + const char *definition; /* Projection definition */ int has_inverse; /* 1 if an inverse mapping exists, 0 otherwise */ double accuracy; /* Expected accuracy of the transformation. -1 if unknown. */ }; @@ -356,7 +358,7 @@ int proj_errno_restore (const PJ *P, int err); PJ_FACTORS proj_factors(PJ *P, LP lp); /* Info functions - get information about various PROJ.4 entities */ -PJ_INFO proj_info(void); +PJ_INFO proj_info(void); PJ_PROJ_INFO proj_pj_info(PJ *P); PJ_GRID_INFO proj_grid_info(const char *gridname); PJ_INIT_INFO proj_init_info(const char *initname); diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 0d50407f..42e3cbf9 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -724,70 +724,113 @@ PJ_CONTEXT *proj_context_destroy (PJ_CONTEXT *ctx) { } + + + + /*****************************************************************************/ -PJ_INFO proj_info(void) { +static char *path_append (char *buf, const char *app, size_t *buf_size) { /****************************************************************************** - Basic info about the current instance of the PROJ.4 library. + Helper for proj_info() below. Append app to buf, separated by a + semicolon. Also handle allocation of longer buffer if needed. + + Returns buffer and adjusts *buf_size through provided pointer arg. +******************************************************************************/ + char *p; + size_t len, applen = 0, buflen = 0; +#ifdef _WIN32 + char *delim = ";"; +#else + char *delim = ":"; +#endif + + /* Nothing to do? */ + if (0 == app) + return buf; + applen = strlen (app); + if (0 == applen) + return buf; + + /* Start checking whether buf is long enough */ + if (0 != buf) + buflen = strlen (buf); + len = buflen+applen+strlen (delim) + 1; + + /* "pj_realloc", so to speak */ + if (*buf_size < len) { + p = pj_calloc (2 * len, sizeof (char)); + if (0==p) { + pj_dealloc (buf); + return 0; + } + *buf_size = 2 * len; + if (buf != 0) + strcpy (p, buf); + pj_dealloc (buf); + buf = p; + } + + /* Only append a semicolon if something's already there */ + if (0 != buflen) + strcat (buf, ";"); + strcat (buf, app); + return buf; +} - Returns PJ_INFO struct. Searchpath member of the struct is truncated to 512 - characters. +static const char *empty = {""}; +static char version[64] = {""}; +static PJ_INFO info = {0, 0, 0, 0, 0, 0, 0, 0}; +static volatile int info_initialized = 0; +/*****************************************************************************/ +PJ_INFO proj_info (void) { +/****************************************************************************** + Basic info about the current instance of the PROJ.4 library. + + Returns PJ_INFO struct. ******************************************************************************/ - PJ_INFO info; const char * const *paths; - char *tmpstr; - int i, n; - size_t len = 0; + size_t i, n; + + size_t buf_size = 0; + char *buf = 0; - memset(&info, 0, sizeof(PJ_INFO)); + pj_acquire_lock (); + + if (0!=info_initialized) { + pj_release_lock (); + return info; + } info.major = PROJ_VERSION_MAJOR; info.minor = PROJ_VERSION_MINOR; info.patch = PROJ_VERSION_PATCH; /* This is a controlled environment, so no risk of sprintf buffer - overflow. A normal version string is xx.yy.zz which is 8 characters - long and there is room for 64 bytes in the version string. */ - sprintf(info.version, "%d.%d.%d", info.major, info.minor, info.patch); - - pj_strlcpy(info.release, pj_get_release(), sizeof(info.release)); + overflow. A normal version string is xx.yy.zz which is 8 characters + long and there is room for 64 bytes in the version string. */ + sprintf (version, "%d.%d.%d", info.major, info.minor, info.patch); + info.searchpath = empty; + info.version = version; + info.release = pj_get_release (); /* build search path string */ - tmpstr = getenv("HOME"); - if (tmpstr != NULL) { - pj_strlcpy(info.searchpath, tmpstr, sizeof(info.searchpath)); - } + buf = path_append (buf, getenv ("HOME"), &buf_size); + buf = path_append (buf, getenv ("PROJ_LIB"), &buf_size); - tmpstr = getenv("PROJ_LIB"); - if (tmpstr != NULL) { - if (strlen(info.searchpath) != 0) { - /* $HOME already in path */ - strcat(info.searchpath, ";"); - len = strlen(tmpstr); - strncat(info.searchpath, tmpstr, sizeof(info.searchpath)-len-1); - } else { - /* path is empty */ - pj_strlcpy(info.searchpath, tmpstr, sizeof(info.searchpath)); - } - } - - paths = proj_get_searchpath(); - n = proj_get_path_count(); + paths = proj_get_searchpath (); + n = (size_t) proj_get_path_count (); - for (i=0; i= 511) - continue; + for (i = 0; i < n; i++) + buf = path_append (buf, paths[i], &buf_size); + info.searchpath = buf ? buf : empty; - if (strlen(info.searchpath) != 0) { - strcat(info.searchpath, ";"); - len = strlen(paths[i]); - strncat(info.searchpath, paths[i], sizeof(info.searchpath)-len-1); - } else { - pj_strlcpy(info.searchpath, paths[i], sizeof(info.searchpath)); - } - } + info.paths = paths; + info.path_count = n; + info_initialized = 1; + pj_release_lock (); return info; } @@ -798,37 +841,41 @@ PJ_PROJ_INFO proj_pj_info(PJ *P) { Basic info about a particular instance of a projection object. Returns PJ_PROJ_INFO struct. - ******************************************************************************/ - PJ_PROJ_INFO info; + PJ_PROJ_INFO pjinfo; char *def; - memset(&info, 0, sizeof(PJ_PROJ_INFO)); + memset(&pjinfo, 0, sizeof(PJ_PROJ_INFO)); /* Expected accuracy of the transformation. Hardcoded for now, will be improved */ /* later. Most likely to be used when a transformation is set up with */ /* proj_create_crs_to_crs in a future version that leverages the EPSG database. */ - info.accuracy = -1.0; + pjinfo.accuracy = -1.0; - if (!P) { - return info; - } + if (0==P) + return pjinfo; /* projection id */ if (pj_param(P->ctx, P->params, "tproj").i) - pj_strlcpy(info.id, pj_param(P->ctx, P->params, "sproj").s, sizeof(info.id)); + pjinfo.id = pj_param(P->ctx, P->params, "sproj").s; /* projection description */ - pj_strlcpy(info.description, P->descr, sizeof(info.description)); + pjinfo.description = P->descr; /* projection definition */ - def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ - pj_strlcpy(info.definition, &def[1], sizeof(info.definition)); /* def includes a leading space */ - pj_dealloc(def); - - info.has_inverse = pj_has_inverse(P); + if (P->def_full) + def = P->def_full; + else + def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ + if (0==def) + pjinfo.definition = empty; + else + pjinfo.definition = pj_shrink (def); + /* Make pj_free clean this up eventually */ + P->def_full = def; - return info; + pjinfo.has_inverse = pj_has_inverse(P); + return pjinfo; } @@ -838,47 +885,49 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) { Information about a named datum grid. Returns PJ_GRID_INFO struct. - ******************************************************************************/ - PJ_GRID_INFO info; + PJ_GRID_INFO grinfo; /*PJ_CONTEXT *ctx = proj_context_create(); */ PJ_CONTEXT *ctx = pj_get_default_ctx(); PJ_GRIDINFO *gridinfo = pj_gridinfo_init(ctx, gridname); - memset(&info, 0, sizeof(PJ_GRID_INFO)); + memset(&grinfo, 0, sizeof(PJ_GRID_INFO)); /* in case the grid wasn't found */ if (gridinfo->filename == NULL) { pj_gridinfo_free(ctx, gridinfo); - strcpy(info.format, "missing"); - return info; + strcpy(grinfo.format, "missing"); + return grinfo; } + /* The string copies below are automatically null-terminated due to */ + /* the memset above, so strncpy is safe */ + /* name of grid */ - pj_strlcpy(info.gridname, gridname, sizeof(info.gridname)); + strncpy (grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); /* full path of grid */ - pj_find_file(ctx, gridname, info.filename, sizeof(info.filename)); + pj_find_file(ctx, gridname, grinfo.filename, sizeof(grinfo.filename) - 1); /* grid format */ - pj_strlcpy(info.format, gridinfo->format, sizeof(info.format)); + strncpy (grinfo.format, gridinfo->format, sizeof(grinfo.format) - 1); /* grid size */ - info.n_lon = gridinfo->ct->lim.lam; - info.n_lat = gridinfo->ct->lim.phi; + grinfo.n_lon = gridinfo->ct->lim.lam; + grinfo.n_lat = gridinfo->ct->lim.phi; /* cell size */ - info.cs_lon = gridinfo->ct->del.lam; - info.cs_lat = gridinfo->ct->del.phi; + grinfo.cs_lon = gridinfo->ct->del.lam; + grinfo.cs_lat = gridinfo->ct->del.phi; /* bounds of grid */ - info.lowerleft = gridinfo->ct->ll; - info.upperright.lam = info.lowerleft.lam + info.n_lon*info.cs_lon; - info.upperright.phi = info.lowerleft.phi + info.n_lat*info.cs_lat; + grinfo.lowerleft = gridinfo->ct->ll; + grinfo.upperright.lam = grinfo.lowerleft.lam + grinfo.n_lon*grinfo.cs_lon; + grinfo.upperright.phi = grinfo.lowerleft.phi + grinfo.n_lat*grinfo.cs_lat; pj_gridinfo_free(ctx, gridinfo); - return info; + return grinfo; } @@ -897,27 +946,28 @@ PJ_INIT_INFO proj_init_info(const char *initname){ If the init file is found, but the metadata is missing, the value is set to "Unknown". - ******************************************************************************/ int file_found; char param[80], key[74]; paralist *start, *next; - PJ_INIT_INFO info; + PJ_INIT_INFO ininfo; PJ_CONTEXT *ctx = pj_get_default_ctx(); - memset(&info, 0, sizeof(PJ_INIT_INFO)); + memset(&ininfo, 0, sizeof(PJ_INIT_INFO)); - file_found = pj_find_file(ctx, initname, info.filename, sizeof(info.filename)); + file_found = pj_find_file(ctx, initname, ininfo.filename, sizeof(ininfo.filename)); if (!file_found || strlen(initname) > 64) { - return info; + return ininfo; } - pj_strlcpy(info.name, initname, sizeof(info.name)); - strcpy(info.origin, "Unknown"); - strcpy(info.version, "Unknown"); - strcpy(info.lastupdate, "Unknown"); + /* The initial memset (0) makes strncpy safe here */ + strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strcpy(ininfo.origin, "Unknown"); + strcpy(ininfo.version, "Unknown"); + strcpy(ininfo.lastupdate, "Unknown"); - pj_strlcpy(key, initname, 64); /* make room for ":metadata\0" at the end */ + strncpy (key, initname, 64); /* make room for ":metadata\0" at the end */ + key[64] = 0; strncat(key, ":metadata", 9); strcpy(param, "+init="); strncat(param, key, 73); @@ -925,24 +975,21 @@ PJ_INIT_INFO proj_init_info(const char *initname){ start = pj_mkparam(param); pj_expand_init(ctx, start); - if (pj_param(ctx, start, "tversion").i) { - pj_strlcpy(info.version, pj_param(ctx, start, "sversion").s, sizeof(info.version)); - } + if (pj_param(ctx, start, "tversion").i) + strncpy(ininfo.version, pj_param(ctx, start, "sversion").s, sizeof(ininfo.version) - 1); - if (pj_param(ctx, start, "torigin").i) { - pj_strlcpy(info.origin, pj_param(ctx, start, "sorigin").s, sizeof(info.origin)); - } + if (pj_param(ctx, start, "torigin").i) + strncpy(ininfo.origin, pj_param(ctx, start, "sorigin").s, sizeof(ininfo.origin) - 1); - if (pj_param(ctx, start, "tlastupdate").i) { - pj_strlcpy(info.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(info.lastupdate)); - } + if (pj_param(ctx, start, "tlastupdate").i) + strncpy(ininfo.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(ininfo.lastupdate) - 1); for ( ; start; start = next) { next = start->next; pj_dalloc(start); } - return info; + return ininfo; } @@ -957,7 +1004,6 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { returns PJ_FACTORS. If unsuccessfull, error number is set and the struct returned contains NULL data. - ******************************************************************************/ PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0, 0,0,0,0}; struct FACTORS f; diff --git a/src/proj_internal.h b/src/proj_internal.h index 3e0ecbe9..bc3b2dd1 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -132,8 +132,6 @@ void proj_fileapi_set (PJ *P, void *fileapi); const char * const *proj_get_searchpath(void); int proj_get_path_count(void); -size_t pj_strlcpy(char *dst, const char *src, size_t siz); - #ifdef __cplusplus } #endif diff --git a/src/projects.h b/src/projects.h index 41f4aa3c..60bac8a1 100644 --- a/src/projects.h +++ b/src/projects.h @@ -231,6 +231,7 @@ struct PJconsts { projCtx_t *ctx; const char *descr; /* From pj_list.h or individual PJ_*.c file */ paralist *params; /* Parameter list */ + char *def_full; /* Full textual definition (usually 0 - set by proj_pj_info) */ char *def_size; /* Shape and size parameters extracted from params */ char *def_shape; char *def_spherification; -- cgit v1.2.3 From 9c75d794177276189d0a2809bc462291e1a070d3 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Fri, 9 Feb 2018 13:43:22 +0100 Subject: Avoid XY, LP and UV datatype clashes with other libraries. Remove unnecessary definitions of UV and UVW from project.h that collides with external libraries. To prevent similar problems in the future the datatypes XY, LP, UV, XYZ, LPZ and UVW has been prefixed by PJ_ in proj.h and proj_internal.h --- src/proj.h | 38 +++++++++++++++++++------------------- src/proj_internal.h | 6 +++--- src/projects.h | 13 +++++++++---- 3 files changed, 31 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/proj.h b/src/proj.h index 73629490..950cc271 100644 --- a/src/proj.h +++ b/src/proj.h @@ -212,13 +212,13 @@ typedef struct { double o, p, k; } PJ_OPK; /* Rotations: omega, phi, k typedef struct { double e, n, u; } PJ_ENU; /* East, North, Up */ /* Classic proj.4 pair/triplet types */ -typedef struct { double u, v; } UV; -typedef struct { double x, y; } XY; -typedef struct { double lam, phi; } LP; +typedef struct { double u, v; } PJ_UV; +typedef struct { double x, y; } PJ_XY; +typedef struct { double lam, phi; } PJ_LP; -typedef struct { double x, y, z; } XYZ; -typedef struct { double u, v, w; } UVW; -typedef struct { double lam, phi, z; } LPZ; +typedef struct { double x, y, z; } PJ_XYZ; +typedef struct { double u, v, w; } PJ_UVW; +typedef struct { double lam, phi, z; } PJ_LPZ; /* Avoid preprocessor renaming and implicit type-punning: Use a union to make it explicit */ @@ -229,12 +229,12 @@ union PJ_COORD { PJ_LPZT lpzt; PJ_OPK opk; PJ_ENU enu; - XYZ xyz; - UVW uvw; - LPZ lpz; - XY xy; - UV uv; - LP lp; + PJ_XYZ xyz; + PJ_UVW uvw; + PJ_LPZ lpz; + PJ_XY xy; + PJ_UV uv; + PJ_LP lp; }; @@ -263,8 +263,8 @@ struct PJ_GRID_INFO { char gridname[32]; /* name of grid */ char filename[260]; /* full path to grid */ char format[8]; /* file format of grid */ - LP lowerleft; /* Coordinates of lower left corner */ - LP upperright; /* Coordinates of upper right corner */ + PJ_LP lowerleft; /* Coordinates of lower left corner */ + PJ_LP upperright; /* Coordinates of upper right corner */ int n_lon, n_lat; /* Grid size */ double cs_lon, cs_lat; /* Cell size of grid */ }; @@ -336,16 +336,16 @@ PJ_COORD proj_coord (double x, double y, double z, double t); double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coo); /* Geodesic distance between two points with angular 2D coordinates */ -double proj_lp_dist (const PJ *P, LP a, LP b); +double proj_lp_dist (const PJ *P, PJ_LP a, PJ_LP b); /* The geodesic distance AND the vertical offset */ -double proj_lpz_dist (const PJ *P, LPZ a, LPZ b); +double proj_lpz_dist (const PJ *P, PJ_LPZ a, PJ_LPZ b); /* Euclidean distance between two points with linear 2D coordinates */ -double proj_xy_dist (XY a, XY b); +double proj_xy_dist (PJ_XY a, PJ_XY b); /* Euclidean distance between two points with linear 3D coordinates */ -double proj_xyz_dist (XYZ a, XYZ b); +double proj_xyz_dist (PJ_XYZ a, PJ_XYZ b); /* Set or read error level */ @@ -355,7 +355,7 @@ int proj_errno_reset (const PJ *P); int proj_errno_restore (const PJ *P, int err); /* Scaling and angular distortion factors */ -PJ_FACTORS proj_factors(PJ *P, LP lp); +PJ_FACTORS proj_factors(PJ *P, PJ_LP lp); /* Info functions - get information about various PROJ.4 entities */ PJ_INFO proj_info(void); diff --git a/src/proj_internal.h b/src/proj_internal.h index bc3b2dd1..071510c7 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -91,9 +91,9 @@ PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo); /* Grid functionality */ int proj_vgrid_init(PJ *P, const char *grids); int proj_hgrid_init(PJ *P, const char *grids); -double proj_vgrid_value(PJ *P, LP lp); -LP proj_hgrid_value(PJ *P, LP lp); -LP proj_hgrid_apply(PJ *P, LP lp, PJ_DIRECTION direction); +double proj_vgrid_value(PJ *P, PJ_LP lp); +PJ_LP proj_hgrid_value(PJ *P, PJ_LP lp); +PJ_LP proj_hgrid_apply(PJ *P, PJ_LP lp, PJ_DIRECTION direction); /* High level functionality for handling thread contexts */ enum proj_log_level { diff --git a/src/projects.h b/src/projects.h index 60bac8a1..bfbe08cb 100644 --- a/src/projects.h +++ b/src/projects.h @@ -157,10 +157,6 @@ typedef struct { double u, v, w; } projUVW; #define XYZ projUVW #define LPZ projUVW -/* Yes, this is ridiculous, but a consequence of an old and bad decision about implicit type-punning through preprocessor abuse */ -typedef struct { double u, v; } UV; -typedef struct { double u, v, w; } UVW; - #else typedef struct { double x, y; } XY; typedef struct { double x, y, z; } XYZ; @@ -169,6 +165,15 @@ typedef struct { double lam, phi, z; } LPZ; typedef struct { double u, v; } UV; typedef struct { double u, v, w; } UVW; #endif /* ndef PJ_LIB__ */ + +#else +typedef PJ_XY XY; +typedef PJ_LP LP; +typedef PJ_UV UV; +typedef PJ_XYZ XYZ; +typedef PJ_LPZ LPZ; +typedef PJ_UVW UVW; + #endif /* ndef PROJ_H */ -- cgit v1.2.3 From 3b9505243e3da8a47aae10bbc294740f01362e7f Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 10 Feb 2018 10:42:03 +0100 Subject: Add BETA2007 grid and change potsdam datum (#383) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functionnaly equivalent to https://github.com/OSGeo/proj.4/pull/371 by Jürgen Fischer See http://crs.bkg.bund.de/crseu/crs/descrtrans/BeTA/de_dhdn2etrs_beta.php Confirmed with Uwe Schmitz that free redistribution is allowed and welcome. --- src/pj_datums.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pj_datums.c b/src/pj_datums.c index 48c77c69..f084f9cc 100644 --- a/src/pj_datums.c +++ b/src/pj_datums.c @@ -47,7 +47,8 @@ C_NAMESPACE_VAR const struct PJ_DATUMS pj_datums[] = { {"NAD27", "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", "clrk66", "North_American_Datum_1927"}, -{"potsdam", "towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7", +{"potsdam", /*"towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7",*/ + "nadgrids=@BETA2007.gsb", "bessel", "Potsdam Rauenberg 1950 DHDN"}, {"carthage","towgs84=-263.0,6.0,431.0", "clrk80ign", -- cgit v1.2.3 From a1ce77b609e7a132ab2b4d4673d8abb6e1f6b28d Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 10 Feb 2018 11:57:12 +0100 Subject: gie: repair handling of angular distances --- src/gie.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 9931b2ef..8d0404b2 100644 --- a/src/gie.c +++ b/src/gie.c @@ -681,7 +681,7 @@ back/forward transformation pairs. banner (T.operation); fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " roundtrip deviation: %.3f mm, expected: %.3f mm\n", 1000*r, 1000*d); + fprintf (T.fout, " roundtrip deviation: %.6f mm, expected: %.6f mm\n", 1000*r, 1000*d); } return another_failure (); } @@ -700,13 +700,13 @@ static int expect_message (double d, const char *args) { fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); fprintf (T.fout, " expected: %s\n", args); - fprintf (T.fout, " got: %.9f %.9f", T.b.xy.x, T.b.xy.y); + fprintf (T.fout, " got: %.12f %.12f", T.b.xy.x, T.b.xy.y); if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0) fprintf (T.fout, " %.9f", T.b.xyz.z); if (T.b.xyzt.t!=0) fprintf (T.fout, " %.9f", T.b.xyzt.t); fprintf (T.fout, "\n"); - fprintf (T.fout, " deviation: %.3f mm, expected: %.3f mm\n", 1000*d, 1000*T.tolerance); + fprintf (T.fout, " deviation: %.6f mm, expected: %.6f mm\n", 1000*d, 1000*T.tolerance); return 1; } @@ -829,42 +829,28 @@ Tell GIE what to expect, when transforming the ACCEPTed input /* expected angular values, probably in degrees */ 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]); + printf ("EXPECTS %.12f %.12f %.12f %.12f\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.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]); + printf ("ACCEPTS %.12f %.12f %.12f %.12f\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 (T.P, T.dir, co): co; if (T.verbosity > 3) - printf ("GOT %.4f %.4f %.4f %.4f\n", co.v[0],co.v[1],co.v[2],co.v[3]); + printf ("GOT %.12f %.12f %.12f %.12f\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)) { - double e = HUGE_VAL; + if (proj_angular_output (T.P, T.dir)) d = proj_lpz_dist (T.P, ce.lpz, co.lpz); - /* check whether input was already in radians */ - if (d > T.tolerance) - e = proj_lpz_dist (T.P, T.e.lpz, co.lpz); - if (e < d) - d = e; - - /* or the tolerance may be based on euclidean distance */ - if (d > T.tolerance) - e = proj_xyz_dist (T.b.xyz, T.e.xyz); - if (e < d) - d = e; - } else d = proj_xyz_dist (T.b.xyz, T.e.xyz); + if (d > T.tolerance) return expect_message (d, args); another_success (); - return 0; } -- cgit v1.2.3 From 82cbab19db5ec8fd7b2fec7f6faf75993381f105 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 10 Feb 2018 12:00:15 +0100 Subject: pj_init: set ellipsoid if given, even if not needed --- src/pj_init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 0acc6c71..f54a2a92 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -593,12 +593,12 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { if (pj_datum_set(ctx, start, PIN)) return pj_default_destructor (PIN, proj_errno(PIN)); - if (PIN->need_ellps) { - int ret = pj_ellipsoid (PIN); - if (0 != ret) { - pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); - return pj_default_destructor (PIN, proj_errno(PIN)); - } + err = pj_ellipsoid (PIN); + if (PIN->need_ellps && 0 != err) { + pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); + return pj_default_destructor (PIN, proj_errno(PIN)); + } + if (0==err) { PIN->a_orig = PIN->a; PIN->es_orig = PIN->es; if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es)) -- cgit v1.2.3 From 797890c920e60ca62f5daeea55dcebb27314fb3c Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 10 Feb 2018 12:16:25 +0100 Subject: Fix numerous bugs in the cs2cs emulation --- src/pj_fwd.c | 14 ++++++++++---- src/pj_inv.c | 17 +++++++++++------ src/proj_4D_api.c | 11 ++++++----- 3 files changed, 27 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/pj_fwd.c b/src/pj_fwd.c index 66b86aab..dbb7748e 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -73,9 +73,11 @@ static PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { 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->helmert, PJ_INV, coo); /* Step into local frame */ coo = proj_trans (P->cart, PJ_INV, coo); /* Go back to angular using local ellps */ } + if (coo.lp.lam==HUGE_VAL) + return coo; if (P->vgridshift) coo = proj_trans (P->vgridshift, PJ_FWD, coo); @@ -92,7 +94,7 @@ static PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD 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 proj_trans (P->helmert, PJ_INV, coo); return coo; } @@ -140,13 +142,17 @@ static PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { if (P->vgridshift) coo = proj_trans (P->vgridshift, PJ_INV, coo); + if (coo.lp.lam==HUGE_VAL) + return coo; if (P->hgridshift) - coo = proj_trans (P->hgridshift, PJ_FWD, coo); + 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->helmert, PJ_INV, coo); /* Step into local frame */ coo = proj_trans (P->cart, PJ_INV, coo); /* Go back to angular using local ellps */ } + if (coo.lp.lam==HUGE_VAL) + return coo; /* If input latitude was geocentrical, convert back to geocentrical */ if (P->geoc) diff --git a/src/pj_inv.c b/src/pj_inv.c index 5e44eb34..7b47f5d7 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -80,11 +80,13 @@ static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { 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->helmert, PJ_FWD, coo); /* Step into WGS84 */ coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ } + if (coo.lp.lam==HUGE_VAL) + return coo; if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_INV, coo); + coo = proj_trans (P->vgridshift, PJ_FWD, coo); return coo; } @@ -99,9 +101,8 @@ static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { 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) { + if (P->is_geocent) coo = proj_trans (P->cart, PJ_INV, coo); - } return coo; @@ -148,14 +149,18 @@ static PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo) { coo.lpz.lam = adjlon(coo.lpz.lam); if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_INV, coo); + coo = proj_trans (P->vgridshift, PJ_FWD, coo); + if (coo.lp.lam==HUGE_VAL) + return 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->helmert, PJ_FWD, coo); /* Step into WGS84 */ coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ } + if (coo.lp.lam==HUGE_VAL) + return coo; } /* If input latitude was geocentrical, convert back to geocentrical */ diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 42e3cbf9..8f6305a4 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -473,12 +473,13 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. def = malloc (100+n); if (0==def) return 0; - sprintf (def, "break_cs2cs_recursion proj=helmert %s", s); + sprintf (def, "break_cs2cs_recursion proj=helmert %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); + P->helmert = skip_prep_fin (Q); break; } @@ -491,14 +492,14 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. Q = proj_create (P->ctx, def); if (0==Q) return 0; - pj_inherit_ellipsoid_def(P, Q); - P->cart = skip_prep_fin(Q); + pj_inherit_ellipsoid_def (P, Q); + 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); + P->cart_wgs84 = skip_prep_fin (Q); } return 1; -- cgit v1.2.3 From 3c62e6e7263b3a0e6e2397db5b43607f4b7fbe1d Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 10 Feb 2018 14:57:50 +0100 Subject: Handle sign convention for vertical datum shifts Also make corresponding sign corrections in a number of tests, and comment out a few tests which work correctly, but report failure since gie is not yet ready to handle unusual axis orders in cases with angular output coordinates. --- src/gie.c | 9 ++++++++- src/pj_fwd.c | 4 ++-- src/pj_inv.c | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 8d0404b2..95f50708 100644 --- a/src/gie.c +++ b/src/gie.c @@ -842,10 +842,17 @@ Tell GIE what to expect, when transforming the ACCEPTed input if (T.verbosity > 3) printf ("GOT %.12f %.12f %.12f %.12f\n", co.v[0],co.v[1],co.v[2],co.v[3]); +#if 0 + /* We need to handle unusual axis orders - that'll be an item for version 5.1 */ + if (T.P->axisswap) { + ce = proj_trans (T.P->axisswap, T.dir, ce); + co = proj_trans (T.P->axisswap, T.dir, co); + } +#endif if (proj_angular_output (T.P, T.dir)) d = proj_lpz_dist (T.P, ce.lpz, co.lpz); else - d = proj_xyz_dist (T.b.xyz, T.e.xyz); + d = proj_xyz_dist (co.xyz, ce.xyz); if (d > T.tolerance) return expect_message (d, args); diff --git a/src/pj_fwd.c b/src/pj_fwd.c index dbb7748e..d238fa4d 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -79,7 +79,7 @@ static PJ_COORD pj_fwd_prepare (PJ *P, PJ_COORD coo) { if (coo.lp.lam==HUGE_VAL) return coo; if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_FWD, coo); + coo = proj_trans (P->vgridshift, PJ_FWD, coo); /* Go orthometric from geometric */ /* Distance from central meridian, taking system zero meridian into account */ coo.lp.lam = (coo.lp.lam - P->from_greenwich) - P->lam0; @@ -141,7 +141,7 @@ static PJ_COORD pj_fwd_finalize (PJ *P, PJ_COORD coo) { coo.lpz.lam = adjlon(coo.lpz.lam); if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_INV, coo); + coo = proj_trans (P->vgridshift, PJ_FWD, coo); /* Go orthometric from geometric */ if (coo.lp.lam==HUGE_VAL) return coo; if (P->hgridshift) diff --git a/src/pj_inv.c b/src/pj_inv.c index 7b47f5d7..4ea88b69 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -86,7 +86,7 @@ static PJ_COORD pj_inv_prepare (PJ *P, PJ_COORD coo) { if (coo.lp.lam==HUGE_VAL) return coo; if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_FWD, coo); + coo = proj_trans (P->vgridshift, PJ_INV, coo); /* Go geometric from orthometric */ return coo; } @@ -149,7 +149,7 @@ static PJ_COORD pj_inv_finalize (PJ *P, PJ_COORD coo) { coo.lpz.lam = adjlon(coo.lpz.lam); if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_FWD, coo); + coo = proj_trans (P->vgridshift, PJ_INV, coo); /* Go geometric from orthometric */ if (coo.lp.lam==HUGE_VAL) return coo; if (P->hgridshift) -- cgit v1.2.3 From 2cca9ead11be47f6efd0ab504cf2e9020b85fa80 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sat, 10 Feb 2018 23:02:47 +0100 Subject: Make the 4D API fully 4D (#788) Make 4D API fully 4D: Remove PJ_XY, PJ_LP, PJ_XYZ, PJ_LPZ etc. from the API surface and make all formal parameters of the API fully 4D PJ_COORD. This operation primarily influences the proj_XXX_dist functions, which mostly work by calling Charles Karney's geodesic subsystem, keeping the distance, and throwing away the start and end azimuths for the geodesic computed. Also a PJ_GEOD(esic) persona is introduced for the PJ_COORD type. The proj_geod function returns a PJ_GEOD, representing the geodesic between the points represented by its PJ_COORD arguments. Finally, the proj_factors functions had its lp argument changed from PJ_LP to PJ_COORD. --- src/gie.c | 12 ++++++------ src/proj.h | 31 +++++++++++++------------------ src/proj_4D_api.c | 47 +++++++++++++++++++++++++++++++---------------- 3 files changed, 50 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 95f50708..5acec578 100644 --- a/src/gie.c +++ b/src/gie.c @@ -850,9 +850,9 @@ Tell GIE what to expect, when transforming the ACCEPTed input } #endif if (proj_angular_output (T.P, T.dir)) - d = proj_lpz_dist (T.P, ce.lpz, co.lpz); + d = proj_lpz_dist (T.P, ce, co); else - d = proj_xyz_dist (co.xyz, ce.xyz); + d = proj_xyz_dist (co, ce); if (d > T.tolerance) return expect_message (d, args); @@ -1432,13 +1432,13 @@ static int pj_horner_selftest (void) { /* Forward projection */ b = proj_trans (P, PJ_FWD, a); - dist = proj_xy_dist (b.xy, c.xy); + dist = proj_xy_dist (b, c); if (dist > 0.001) return 2; /* Inverse projection */ b = proj_trans (P, PJ_INV, c); - dist = proj_xy_dist (b.xy, a.xy); + dist = proj_xy_dist (b, a); if (dist > 0.001) return 3; @@ -1520,7 +1520,7 @@ static int pj_cart_selftest (void) { /* Forward again, to get two linear items for comparison */ a = proj_trans (P, PJ_FWD, a); - dist = proj_xy_dist (a.xy, b.xy); + dist = proj_xy_dist (a, b); if (dist > 2e-9) return 3; @@ -1771,7 +1771,7 @@ static int pj_cart_selftest (void) { a.lp.lam = PJ_TORAD(12); a.lp.phi = PJ_TORAD(55); - factors = proj_factors(P, a.lp); + factors = proj_factors(P, a); if (proj_errno(P)) return 85; /* factors not created correctly */ diff --git a/src/proj.h b/src/proj.h index 950cc271..8997965e 100644 --- a/src/proj.h +++ b/src/proj.h @@ -112,13 +112,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -/* -#ifdef _MSC_VER -#ifndef _USE_MATH_DEFINES -#define _USE_MATH_DEFINES -#endif -#endif -#include For M_PI */ + #include /* For size_t */ @@ -151,11 +145,6 @@ typedef union PJ_COORD PJ_COORD; struct PJ_AREA; typedef struct PJ_AREA PJ_AREA; -/* The slimmed down PROJ 5.0.0 version of struct FACTORS */ -/* Will take over the world and the name when we can rid */ -/* the library for deprecated stuff, but it's the typedef */ -/* which is userspace useful, so it does not do much of a */ -/* difference */ struct P5_FACTORS { /* Common designation */ double meridional_scale; /* h */ double parallel_scale; /* k */ @@ -210,8 +199,9 @@ typedef struct { double u, v, w, t; } PJ_UVWT; typedef struct { double lam, phi, z, t; } PJ_LPZT; typedef struct { double o, p, k; } PJ_OPK; /* Rotations: omega, phi, kappa */ typedef struct { double e, n, u; } PJ_ENU; /* East, North, Up */ +typedef struct { double s, a1, a2; } PJ_GEOD; /* Geodesic length, fwd azi, rev azi */ -/* Classic proj.4 pair/triplet types */ +/* Classic proj.4 pair/triplet types - moved into the PJ_ name space */ typedef struct { double u, v; } PJ_UV; typedef struct { double x, y; } PJ_XY; typedef struct { double lam, phi; } PJ_LP; @@ -227,6 +217,7 @@ union PJ_COORD { PJ_XYZT xyzt; PJ_UVWT uvwt; PJ_LPZT lpzt; + PJ_GEOD geod; PJ_OPK opk; PJ_ENU enu; PJ_XYZ xyz; @@ -336,16 +327,20 @@ PJ_COORD proj_coord (double x, double y, double z, double t); double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coo); /* Geodesic distance between two points with angular 2D coordinates */ -double proj_lp_dist (const PJ *P, PJ_LP a, PJ_LP b); +double proj_lp_dist (const PJ *P, PJ_COORD a, PJ_COORD b); /* The geodesic distance AND the vertical offset */ -double proj_lpz_dist (const PJ *P, PJ_LPZ a, PJ_LPZ b); +double proj_lpz_dist (const PJ *P, PJ_COORD a, PJ_COORD b); /* Euclidean distance between two points with linear 2D coordinates */ -double proj_xy_dist (PJ_XY a, PJ_XY b); +double proj_xy_dist (PJ_COORD a, PJ_COORD b); /* Euclidean distance between two points with linear 3D coordinates */ -double proj_xyz_dist (PJ_XYZ a, PJ_XYZ b); +double proj_xyz_dist (PJ_COORD a, PJ_COORD b); + +/* Geodesic distance (in meter) + fwd and rev azimuth between two points on the ellipsoid */ +PJ_COORD proj_geod (const PJ *P, PJ_COORD a, PJ_COORD b); + /* Set or read error level */ @@ -355,7 +350,7 @@ int proj_errno_reset (const PJ *P); int proj_errno_restore (const PJ *P, int err); /* Scaling and angular distortion factors */ -PJ_FACTORS proj_factors(PJ *P, PJ_LP lp); +PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp); /* Info functions - get information about various PROJ.4 entities */ PJ_INFO proj_info(void); diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 8f6305a4..2f689085 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -67,32 +67,47 @@ int proj_angular_output (PJ *P, enum PJ_DIRECTION dir) { } +/* Geodesic distance (in meter) + fwd and rev azimuth between two points on the ellipsoid */ +PJ_COORD proj_geod (const PJ *P, PJ_COORD a, PJ_COORD b) { + PJ_COORD c; + /* Note: the geodesic code takes arguments in degrees */ + geod_inverse (P->geod, + PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), + PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), + c.v, c.v+1, c.v+2 + ); + + return c; +} + + /* Geodesic distance (in meter) between two points with angular 2D coordinates */ -double proj_lp_dist (const PJ *P, LP a, LP b) { +double proj_lp_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { double s12, azi1, azi2; /* Note: the geodesic code takes arguments in degrees */ - geod_inverse (P->geod, PJ_TODEG(a.phi), PJ_TODEG(a.lam), PJ_TODEG(b.phi), PJ_TODEG(b.lam), &s12, &azi1, &azi2); + geod_inverse (P->geod, + PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), + PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), + &s12, &azi1, &azi2 + ); return s12; } /* The geodesic distance AND the vertical offset */ -double proj_lpz_dist (const PJ *P, LPZ a, LPZ b) { - PJ_COORD aa, bb; - aa.lpz = a; - bb.lpz = b; - if (HUGE_VAL==a.lam || HUGE_VAL==b.lam) +double proj_lpz_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { + if (HUGE_VAL==a.lpz.lam || HUGE_VAL==b.lpz.lam) return HUGE_VAL; - return hypot (proj_lp_dist (P, aa.lp, bb.lp), a.z - b.z); + return hypot (proj_lp_dist (P, a, b), a.lpz.z - b.lpz.z); } /* Euclidean distance between two points with linear 2D coordinates */ -double proj_xy_dist (XY a, XY b) { - return hypot (a.x - b.x, a.y - b.y); +double proj_xy_dist (PJ_COORD a, PJ_COORD b) { + return hypot (a.xy.x - b.xy.x, a.xy.y - b.xy.y); } /* Euclidean distance between two points with linear 3D coordinates */ -double proj_xyz_dist (XYZ a, XYZ b) { - return hypot (hypot (a.x - b.x, a.y - b.y), a.z - b.z); +double proj_xyz_dist (PJ_COORD a, PJ_COORD b) { + return hypot (proj_xy_dist (a, b), a.xyz.z - b.xyz.z); } @@ -125,9 +140,9 @@ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coo) { /* checking for angular *input* since we do a roundtrip, and end where we begin */ if (proj_angular_input (P, direction)) - return proj_lpz_dist (P, org.lpz, t.lpz); + return proj_lpz_dist (P, org, t); - return proj_xyz_dist (org.xyz, t.xyz); + return proj_xyz_dist (org, t); } @@ -996,7 +1011,7 @@ PJ_INIT_INFO proj_init_info(const char *initname){ /*****************************************************************************/ -PJ_FACTORS proj_factors(PJ *P, LP lp) { +PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { /****************************************************************************** Cartographic characteristics at point lp. @@ -1012,7 +1027,7 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { if (0==P) return factors; - if (pj_factors(lp, P, 0.0, &f)) + if (pj_factors(lp.lp, P, 0.0, &f)) return factors; factors.meridional_scale = f.h; -- cgit v1.2.3 From 0199b3fd17fca361bba4eec93209c0b74c0f7340 Mon Sep 17 00:00:00 2001 From: Charles Karney Date: Sat, 10 Feb 2018 17:17:27 -0500 Subject: Re-do pull request #451 export CMake targets with PROJ4:: namespace This version takes to add the include path to the target definition for cmake 2.8.11 and later. Also the documentation sticks to the existing convention of using cmake variables ${PROJ4_LIBRARIES} and ${PROJ4_INCLUDE_DIRS}. However, the namespace variables are still being included. Here's the roll-out plan (0) Version 4.9.x: The target is proj and PROJ4_LIBRARIES is set to this. (1) Version 5.0.x: Two targets, proj and PROJ4::proj, are defined; PROJ4_LIBRARIES = proj. (2) In a year or two: Two targets, proj and PROJ4::proj, are defined; PROJ4_LIBRARIES = PROJ4::proj. (3) With a change in the library which breaks backwards compatibility: The target is PROJ4::proj and PROJ4_LIBRARIES = PROJ4::proj. --- src/lib_proj.cmake | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index f1337a54..c9e4d9e6 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -284,6 +284,10 @@ add_library( ${PROJ_CORE_TARGET} ${ALL_LIBPROJ_HEADERS} ${PROJ_RESOURCES} ) +if (NOT CMAKE_VERSION VERSION_LESS 2.8.11) + target_include_directories (${PROJ_CORE_TARGET} INTERFACE + $) +endif () if(WIN32) set_target_properties(${PROJ_CORE_TARGET} -- cgit v1.2.3 From 06d35126d36f377e06da0a747cf0847e1827d1be Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Sat, 10 Feb 2018 23:25:36 +0100 Subject: Make sure that #define's work properly when including proj_api.h and proj.h together. --- src/proj_api.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/proj_api.h b/src/proj_api.h index 40c40ad1..5bba5887 100644 --- a/src/proj_api.h +++ b/src/proj_api.h @@ -25,8 +25,6 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ - - /* * This version number should be updated with every release! * @@ -84,10 +82,18 @@ extern int pj_errno; /* global error return code */ /* These make the function declarations below conform with classic proj */ typedef PJ *projPJ; /* projPJ is a pointer to PJ */ typedef struct projCtx_t *projCtx; /* projCtx is a pointer to projCtx_t */ -# define projXY XY +#ifdef PROJ_H +# define projXY PJ_XY +# define projLP PJ_LP +# define projXYZ PJ_XYZ +# define projLPZ PJ_LPZ +#else +# define projXY XY # define projLP LP # define projXYZ XYZ # define projLPZ LPZ +#endif + #else /* i.e. proj_api invoked as primary API */ typedef struct { double u, v; } projUV; -- cgit v1.2.3 From e1a213b2d9a29d86b89547781a2e27d5fd01f111 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sun, 11 Feb 2018 00:42:05 +0100 Subject: Add proj_context_errno to API and missing proj_geod to proj.def (#791) --- src/proj.def | 3 +++ src/proj.h | 1 + src/proj_4D_api.c | 15 +++++++++++++++ 3 files changed, 19 insertions(+) (limited to 'src') diff --git a/src/proj.def b/src/proj.def index f2936edb..d6c84dc3 100644 --- a/src/proj.def +++ b/src/proj.def @@ -152,3 +152,6 @@ EXPORTS proj_angular_input @138 proj_angular_output @139 + + proj_geod @140 + proj_context_errno @141 diff --git a/src/proj.h b/src/proj.h index 8997965e..133f1412 100644 --- a/src/proj.h +++ b/src/proj.h @@ -344,6 +344,7 @@ PJ_COORD proj_geod (const PJ *P, PJ_COORD a, PJ_COORD b); /* Set or read error level */ +int proj_context_errno (PJ_CONTEXT *ctx); int proj_errno (const PJ *P); int proj_errno_set (const PJ *P, int err); int proj_errno_reset (const PJ *P); diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 2f689085..796e5769 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -649,10 +649,25 @@ PJ *proj_destroy (PJ *P) { return 0; } +/*****************************************************************************/ int proj_errno (const PJ *P) { +/****************************************************************************** + Read an error level from the context of a PJ. +******************************************************************************/ return pj_ctx_get_errno (pj_get_ctx ((PJ *) P)); } +/*****************************************************************************/ +int proj_context_errno (PJ_CONTEXT *ctx) { +/****************************************************************************** + Read an error directly from a context, without going through a PJ + belonging to that context. +******************************************************************************/ + if (0==ctx) + ctx = pj_get_default_ctx(); + return pj_ctx_get_errno (ctx); +} + /*****************************************************************************/ int proj_errno_set (const PJ *P, int err) { /****************************************************************************** -- cgit v1.2.3 From 332bf648a65b5a1a6bb1e2f5d7f5c7cca1ce1159 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Sun, 11 Feb 2018 16:07:18 +0100 Subject: Add missing ! in boolean evaluation. Fixes #780. --- src/pj_gridinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pj_gridinfo.c b/src/pj_gridinfo.c index b1b39e01..b855980b 100644 --- a/src/pj_gridinfo.c +++ b/src/pj_gridinfo.c @@ -564,7 +564,7 @@ static int pj_gridinfo_init_ntv2( projCtx ctx, PAFile fid, PJ_GRIDINFO *gilist ) gi->gridname = pj_strdup( gilist->gridname ); gi->filename = pj_strdup( gilist->filename ); - if (!gi->gridname || gi->filename) { + if (!gi->gridname || !gi->filename) { pj_gridinfo_free(ctx, gi); pj_dalloc(ct); pj_gridinfo_free(ctx, gilist); -- cgit v1.2.3 From cc26b288fe0a208468068726bc4b38f4cf05ba47 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sun, 11 Feb 2018 17:29:17 +0100 Subject: typedef some recurring function signatures --- src/pj_init.c | 10 ++++------ src/projects.h | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index f54a2a92..3c36fc3a 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -470,22 +470,20 @@ pj_init(int argc, char **argv) { } -typedef PJ *(constructor)(PJ *); - -static constructor *pj_constructor (const char *name) { +static PJ_CONSTRUCTOR pj_locate_constructor (const char *name) { int i; char *s; for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ; if (0==s) return 0; - return (constructor *) pj_list[i].proj; + return (PJ_CONSTRUCTOR) pj_list[i].proj; } PJ * pj_init_ctx(projCtx ctx, int argc, char **argv) { char *s, *name; - constructor *proj; + PJ_CONSTRUCTOR proj; paralist *curr, *init, *start; int i; int err; @@ -559,7 +557,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); name += 5; - proj = pj_constructor (name); + proj = pj_locate_constructor (name); if (0==proj) return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); diff --git a/src/projects.h b/src/projects.h index bfbe08cb..25ff82a4 100644 --- a/src/projects.h +++ b/src/projects.h @@ -218,6 +218,37 @@ struct PJ_AREA { struct projCtx_t; typedef struct projCtx_t projCtx_t; +/***************************************************************************** + + Some function types that are especially useful when working with PJs + +****************************************************************************** + +PJ_CONSTRUCTOR: + + A function taking a pointer-to-PJ as arg, and returning a pointer-to-PJ. + Historically called twice: First with a 0 argument, to allocate memory, + second with the first return value as argument, for actual setup. + +PJ_DESTRUCTOR: + + A function taking a pointer-to-PJ and an integer as args, then first + handling the deallocation of the PJ, afterwards handing the integer over + to the error reporting subsystem, and finally returning a null pointer in + support of the "return free (P)" (aka "get the hell out of here") idiom. + +PJ_OPERATOR: + + A function taking a PJ_COORD and a pointer-to-PJ as args, applying the + PJ to the PJ_COORD, and returning the resulting PJ_COORD. + +*****************************************************************************/ +typedef PJ *(* PJ_CONSTRUCTOR) (PJ *); +typedef void *(* PJ_DESTRUCTOR) (PJ *, int); +typedef PJ_COORD (* PJ_OPERATOR) (PJ_COORD, PJ *); +/****************************************************************************/ + + /* base projection data structure */ struct PJconsts { @@ -267,10 +298,10 @@ struct PJconsts { LP (*inv)(XY, PJ *); XYZ (*fwd3d)(LPZ, PJ *); LPZ (*inv3d)(XYZ, PJ *); - PJ_COORD (*fwd4d)(PJ_COORD, PJ *); - PJ_COORD (*inv4d)(PJ_COORD, PJ *); + PJ_OPERATOR fwd4d; + PJ_OPERATOR inv4d; - void *(*destructor)(PJ *, int); + PJ_DESTRUCTOR destructor; /************************************************************************************* -- cgit v1.2.3 From d3cc97a0cce7f01cf878be4ace8ac0bef538dd39 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sun, 11 Feb 2018 17:33:00 +0100 Subject: a minor linguistic correction --- src/pj_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index 3c36fc3a..be3a2457 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -509,7 +509,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { n_inits++; } - /* can't have nested pipeline directly */ + /* can't have nested pipelines directly */ if (n_pipelines > 1) { pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE); return 0; -- cgit v1.2.3 From 5480a1ffaa38e16dd592beeaa42deb7e2cf4eadb Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Sun, 11 Feb 2018 19:13:10 +0100 Subject: Repair prior attempt to default to WGS84 if explicit ellps not needed --- src/gie.c | 13 +++++++------ src/pj_init.c | 25 ++++++++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/gie.c b/src/gie.c index 5acec578..577ad7cf 100644 --- a/src/gie.c +++ b/src/gie.c @@ -1836,12 +1836,12 @@ static int pj_cart_selftest (void) { /* linear in and out */ P = proj_create(PJ_DEFAULT_CTX, - " +proj=helmert +ellps=GRS80" + " +proj=helmert" " +x=0.0127 +y=0.0065 +z=-0.0209 +s=0.00195" " +rx=-0.00039 +ry=0.00080 +rz=-0.00114" " +dx=-0.0029 +dy=-0.0002 +dz=-0.0006 +ds=0.00001" " +drx=-0.00011 +dry=-0.00019 +drz=0.00007" - " +t_epoch=1988.0 +transpose" + " +t_epoch=1988.0 +transpose +no_defs" ); if (0==P) return 0; if (proj_angular_input (P, PJ_FWD)) return 116; @@ -1853,8 +1853,12 @@ static int pj_cart_selftest (void) { if (proj_angular_input (P, PJ_INV)) return 121; if (proj_angular_output (P, PJ_FWD)) return 122; if (proj_angular_output (P, PJ_INV)) return 123; - proj_destroy(P); + /* We specified "no_defs" but didn't give any ellipsoid info */ + /* pj_init_ctx should defualt to WGS84 */ + if (P->a != 6378137.0) return 124; + if (P->f != 1.0/298.257223563) return 125; + proj_destroy(P); return 0; } @@ -1862,9 +1866,6 @@ static int pj_cart_selftest (void) { - - - static int test_time(const char* args, double tol, double t_in, double t_exp) { PJ_COORD in, out; PJ *P = proj_create(PJ_DEFAULT_CTX, args); diff --git a/src/pj_init.c b/src/pj_init.c index be3a2457..bf098686 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -592,16 +592,23 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { return pj_default_destructor (PIN, proj_errno(PIN)); err = pj_ellipsoid (PIN); - if (PIN->need_ellps && 0 != err) { - pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); - return pj_default_destructor (PIN, proj_errno(PIN)); - } - if (0==err) { - 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); + + 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 { + 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 -- cgit v1.2.3 From f1dd367c64de4f48eb48f6dcf17c2be93c7eaafc Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 12 Feb 2018 16:45:09 +0100 Subject: Avoid invalid ellps error messages from pj_init (#794) Reset error type PJD_ERR_MAJOR_AXIS_NOT_GIVEN for operations that do not need an ellipsoid. --- src/pj_init.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/pj_init.c b/src/pj_init.c index bf098686..78486480 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -600,6 +600,8 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { 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); -- cgit v1.2.3 From 60566a2239328d83ba809b3673a852ba7eab3690 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 12 Feb 2018 16:46:24 +0100 Subject: Make proj_pj_info work correctly for pipelines (#795) Due to the slightly involved way a pipeline is set up, only a small subset of the definition parameters are directly read by the pj_init code. The remaining parameters will not get their "used" flag set, and for that reason will not be included in the projection definition element of a PJ_PROJ_INFO, returned by proj_pj_info. For now, we force the "used" flag of all elements of a pipeline to be set. The code is tested by introducing cct functionality for printing the projection definition used. --- src/PJ_pipeline.c | 10 ++++++---- src/cct.c | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index 2f904ab1..25c7a953 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -240,11 +240,13 @@ static PJ *pj_create_pipeline (PJ *P, size_t steps) { -/* count the number of args in pipeline definition */ +/* count the number of args in pipeline definition, and mark all args as used */ static size_t argc_params (paralist *params) { size_t argc = 0; - for (; params != 0; params = params->next) + for (; params != 0; params = params->next) { argc++; + params->used = 1; + } return ++argc; /* one extra for the sentinel */ } @@ -414,7 +416,7 @@ PJ *OPERATION(pipeline,0) { err = proj_errno_reset (P); next_step = proj_create_argv (P->ctx, current_argc, current_argv); - proj_log_trace (P, "Pipeline: Step %d at %p", i, next_step); + proj_log_trace (P, "Pipeline: Step %d (%s) at %p", i, current_argv[0], next_step); if (0==next_step) { /* The step init failed, but possibly without setting errno. If so, we say "malformed" */ @@ -436,7 +438,7 @@ PJ *OPERATION(pipeline,0) { P->opaque->pipeline[i+1] = next_step; - proj_log_trace (P, "Pipeline at [%p]: step at [%p] done", P, next_step); + proj_log_trace (P, "Pipeline at [%p]: step at [%p] (%s) done", P, next_step, current_argv[0]); } /* Require a forward path through the pipeline */ diff --git a/src/cct.c b/src/cct.c index 2ce478bd..dc68122d 100644 --- a/src/cct.c +++ b/src/cct.c @@ -151,6 +151,7 @@ static const char usage[] = { int main(int argc, char **argv) { PJ *P; PJ_COORD point; + PJ_PROJ_INFO info; OPTARGS *o; FILE *fout = stdout; char *buf; @@ -222,6 +223,11 @@ int main(int argc, char **argv) { return 1; } + if (verbose > 4) { + info = proj_pj_info (P); + fprintf (stdout, "Final: %s argc=%d pargc=%d\n", info.definition, argc, o->pargc); + } + if (direction==-1) { /* fail if an inverse operation is not available */ if (!proj_pj_info(P).has_inverse) { -- cgit v1.2.3 From 715ec0b90aec05f15554c6591ea8cfd09d5cf043 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Mon, 12 Feb 2018 20:44:00 +0100 Subject: Ensure cs2cs emulation gets correct ellipsoid defn (#798) Copy ellipsoid definition for proj=cart directly into the proj_create call, rather than calling pj_inherit_ellipsoid_def afterwards. Previously, the ellipsoid definition was left out from the call. pj_init_ctx would then pick up WGS84 from proj_def.dat, and the init would succeed (and the possibly wrong ellipsoid definition would later on be overwritten with the correct values by pj_inherit_ellipsoid_def. But if PROJ_LIB was not set or proj_def.dat was inaccessible for other reasons, things went wrong. --- src/pj_geocent.c | 2 +- src/proj_4D_api.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pj_geocent.c b/src/pj_geocent.c index 6530b103..3d771c43 100644 --- a/src/pj_geocent.c +++ b/src/pj_geocent.c @@ -48,7 +48,7 @@ static LP inverse(XY xy, PJ *P) { return lp; } -PJ *PROJECTION(geocent) { +PJ *CONVERSION (geocent, 0) { P->is_geocent = 1; P->x0 = 0.0; P->y0 = 0.0; diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index 796e5769..dab4b409 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -414,6 +414,8 @@ 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. + +Returns 1 on success, 0 on failure **************************************************************************************/ PJ *Q; paralist *p; @@ -502,12 +504,11 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. /* 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 def[100]; - sprintf (def, "break_cs2cs_recursion proj=cart"); + char def[150]; + sprintf (def, "break_cs2cs_recursion proj=cart a=%40.20g f=%40.20g", P->a, P->f); 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 ellps=WGS84"); @@ -521,6 +522,7 @@ invocators can emulate the behaviour of pj_transform and the cs2cs app. } + /*************************************************************************************/ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { /************************************************************************************** -- cgit v1.2.3 From 4ce25c3762f1247916a51e0bda081bb3d738263a Mon Sep 17 00:00:00 2001 From: Raul Marin Date: Mon, 19 Feb 2018 18:13:58 +0100 Subject: proj_errno_reset: Also reset pj_errno --- src/proj_4D_api.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c index dab4b409..fb20978b 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -734,6 +734,7 @@ int proj_errno_reset (const PJ *P) { pj_ctx_set_errno (pj_get_ctx ((PJ *) P), 0); errno = 0; + pj_errno = 0; return last_errno; } -- cgit v1.2.3