diff options
Diffstat (limited to 'src')
82 files changed, 3450 insertions, 2093 deletions
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.am b/src/Makefile.am index 096cc672..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 12:0:0 +libproj_la_LDFLAGS = -no-undefined -version-info 13:0:0 libproj_la_SOURCES = \ pj_list.h proj_internal.h\ @@ -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_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_axisswap.c b/src/PJ_axisswap.c index f8f17380..44446d9c 100644 --- a/src/PJ_axisswap.c +++ b/src/PJ_axisswap.c @@ -159,43 +159,86 @@ 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); - } + + /* +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; i<strlen(order); i++) + if (strchr("1234-,", order[i]) == 0) { + proj_log_error(P, "axisswap: unknown axis '%c'", order[i]); + return pj_default_destructor(P, PJD_ERR_AXIS); + } - /* check that all characters are valid */ - for (i=0; i<strlen(order); i++) - if (strchr("1234-,", order[i]) == 0) { - proj_log_error(P, "axisswap: unknown axis '%c'", order[i]); - return pj_default_destructor(P, PJD_ERR_AXIS); + /* 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] > 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++; + 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; + 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); + } } - Q->sign[n++] = sign(atoi(s)); - while ( *s != '\0' && *s != ',' ) - s++; - if ( *s == ',' ) - s++; + n = 3; } /* check for duplicate axes */ @@ -209,6 +252,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; @@ -223,19 +267,29 @@ 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); } 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_METERS; - P->right = PJ_IO_UNITS_METERS; + P->left = PJ_IO_UNITS_PROJECTED; + 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; } - 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_cart.c b/src/PJ_cart.c index 12b5876a..0746ec08 100644 --- a/src/PJ_cart.c +++ b/src/PJ_cart.c @@ -213,8 +213,7 @@ PJ *CONVERSION(cart,1) { P->inv3d = geodetic; P->fwd = cart_forward; P->inv = cart_reverse; - P->left = PJ_IO_UNITS_RADIANS; - P->right = PJ_IO_UNITS_METERS; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_CARTESIAN; return P; } - 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_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 <proj.h> #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; } diff --git a/src/PJ_deformation.c b/src/PJ_deformation.c index 09692ccb..797af006 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; @@ -280,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_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_geoc.c b/src/PJ_geoc.c index 865b1089..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); } @@ -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_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_helmert.c b/src/PJ_helmert.c index 34bb7a68..229e30c2 100644 --- a/src/PJ_helmert.c +++ b/src/PJ_helmert.c @@ -73,8 +73,8 @@ struct pj_opaque_helmert { double theta_0; double dtheta; double R[3][3]; - double epoch, t_obs; - int no_rotation, approximate, transpose, fourparam; + double t_epoch, t_obs; + int no_rotation, exact, 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); @@ -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) @@ -474,8 +477,33 @@ 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; + } + + /* 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) @@ -539,15 +567,15 @@ 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) - 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) @@ -561,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 approximate=% d transpose=% d", - 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, "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/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_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_isea.c b/src/PJ_isea.c index bf006a62..5cc71c08 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}, @@ -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 @@ -217,7 +213,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}, @@ -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_latlong.c b/src/PJ_latlong.c index 5919023a..1331d59a 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 <proj.h> #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) { - 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; + +static PJ *latlong_setup (PJ *P) { + P->is_latlong = 1; + 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_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; 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_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_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_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_ob_tran.c b/src/PJ_ob_tran.c index 4ce4bd4d..c447ac08 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_ANGULAR) + P->right = PJ_IO_UNITS_PROJECTED; + + return P; } 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); diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index c8ce8582..25c7a953 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: @@ -75,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"), @@ -99,6 +96,7 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 #define PJ_LIB__ #include <geodesic.h> +#include <proj.h> #include "proj_internal.h" #include "projects.h" @@ -110,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; @@ -127,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; @@ -187,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; @@ -201,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) @@ -243,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); @@ -267,16 +238,20 @@ 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 */ } /* 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) { @@ -291,6 +266,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. */ @@ -338,18 +316,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) @@ -366,7 +355,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); @@ -379,7 +368,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; @@ -427,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" */ @@ -442,12 +431,39 @@ 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 (0==strcmp("inv", current_argv[j])) { + /* 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; - 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 */ + 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++) { + PJ *Q = P->opaque->pipeline[i]; + if ( pj_has_inverse(Q) ) { + 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); 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; } 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_unitconvert.c b/src/PJ_unitconvert.c index f951ebb6..06224362 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) { @@ -94,6 +101,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,22 +221,55 @@ static double mjd_to_gps_week(double mjd) { return (mjd - 44244.0) / 7.0; } -struct TIME_UNITS time_units[] = { + +/***********************************************************************/ +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.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"}, {"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} }; -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) { @@ -207,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; } @@ -223,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; } @@ -242,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; } @@ -259,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; } @@ -271,7 +344,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); @@ -291,7 +364,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); @@ -311,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); @@ -323,47 +397,70 @@ 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_WHATEVER; + P->right = PJ_IO_UNITS_WHATEVER; /* if no time input/output unit is specified we can skip them */ 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; } @@ -388,4 +485,3 @@ PJ *CONVERSION(unitconvert,0) { return P; } - 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/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/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 <projects.h> #define ONE_TOL 1.00000000000001 -#define TOL 0.000000001 #define ATOL 1e-50 double @@ -71,20 +71,23 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26 ***********************************************************************/ -#include "optargpm.h" -#include "proj_internal.h" -#include <proj.h> -#include "projects.h" +#include <ctype.h> +#include <math.h> #include <stdio.h> #include <stdlib.h> -#include <ctype.h> #include <string.h> -#include <math.h> + +#include <proj.h> +#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); @@ -148,13 +151,14 @@ 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; 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); @@ -170,6 +174,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) { @@ -193,7 +202,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); @@ -214,9 +223,20 @@ 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 (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) { + 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 */ 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 <stdio.h> #include <stdlib.h> @@ -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/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 <errno.h> #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'); @@ -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 <string.h> +#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/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 6899436c..0f2c0ac2 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, @@ -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; } @@ -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,52 @@ Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08 ***********************************************************************/ -#include "optargpm.h" +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <proj.h> #include "proj_internal.h" #include "projects.h" -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> - -#include <string.h> -#include <ctype.h> +#include "optargpm.h" -#include <math.h> -#include <errno.h> +/* 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; + +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[] = { + "<gie>", "operation", "accept", "expect", "roundtrip", "banner", "verbose", + "direction", "tolerance", "ignore", "builtins", "echo", "</gie>" +}; +static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0]; /* from proj_strtod.c */ @@ -127,16 +158,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,33 +177,24 @@ 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; - 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; -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; - -size_t tol_lineno = 0; -size_t lineno = 0; -size_t level = 0; -char delim[] = {"-------------------------------------------------------------------------------\n"}; -char DELIM[] = {"===============================================================================\n"}; - - -#define CMDLEN 25000 - -int nfiles = 0; +static const char delim[] = {"-------------------------------------------------------------------------------\n"}; static const char usage[] = { @@ -210,8 +230,15 @@ 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; + + memset (&T, 0, sizeof (T)); + 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) @@ -222,8 +249,15 @@ 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")) - list_err_codes (); + return list_err_codes (); T.verbosity = opt_given (o, "q"); @@ -235,6 +269,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,15 +280,24 @@ 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) { 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 @@ -264,6 +308,7 @@ int main (int argc, char **argv) { fclose (T.fout); free (o); + ffio_destroy (F); return T.grand_ko; } @@ -273,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++; @@ -282,13 +333,11 @@ 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; + T.op_skip = T.total_skip = 0; f = fopen (fname, "rt"); if (0==f) { @@ -298,37 +347,31 @@ 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; + 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 (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 '<gie>' cmnd - bye!\n", fname); + if (F->level && F->level%2) + return errmsg (-4, "File '%s':Missing '</gie>' cmnd - bye!\n", fname); return 0; } @@ -336,7 +379,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,10 +399,11 @@ const char *column (const char *buf, int n) { /*****************************************************************************/ static double strtod_scaled (const char *args, double default_scale) { /***************************************************************************** - Interpret <args> as a numeric followed by a linear decadal prefix. - Return the properly scaled numeric +Interpret <args> as a numeric followed by a linear decadal prefix. +Return the properly scaled numeric ******************************************************************************/ double s; + const double GRS80_DEG = 111319.4908; /* deg-to-m at equator of GRS80 */ const char *endp = args; s = proj_strtod (args, (char **) &endp); if (args==endp) @@ -381,6 +425,10 @@ static double strtod_scaled (const char *args, double default_scale) { 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; @@ -405,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; @@ -424,13 +476,15 @@ static int direction (const char *args) { default: return 1; } + return 0; } 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; } @@ -439,46 +493,40 @@ 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; + T.op_skip = 0; direction ("forward"); tolerance ("0.5 mm"); + ignore ("pjd_err_dont_skip"); + 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 +541,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 +552,10 @@ static int builtins (const char *args) { } T.op_ok = 0; T.op_ko = 0; - + T.op_skip = 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 +564,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 +572,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 @@ -534,19 +582,29 @@ static int builtins (const char *args) { } -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; } @@ -554,16 +612,19 @@ 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; 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; } @@ -575,11 +636,12 @@ 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) printf ("# %s\n", args); + T.dimensions_given_at_last_accept = T.dimensions_given; return 0; } @@ -587,25 +649,28 @@ 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; 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)); 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; + 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) @@ -615,8 +680,8 @@ 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, " roundtrip deviation: %.3f mm, expected: %.3f mm\n", 1000*r, 1000*d); + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); + fprintf (T.fout, " roundtrip deviation: %.6f mm, expected: %.6f mm\n", 1000*r, 1000*d); } return another_failure (); } @@ -633,15 +698,15 @@ 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); + 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; } @@ -652,7 +717,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 +730,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"); @@ -673,10 +738,23 @@ 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) { /***************************************************************************** - 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; @@ -691,12 +769,16 @@ static int expect (const char *args) { 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 (); } @@ -712,8 +794,8 @@ static int expect (const char *args) { 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; - co = proj_trans (T.P, T.dir, ci); + 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 */ if (co.xyz.x!=HUGE_VAL) @@ -737,6 +819,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); @@ -744,45 +827,37 @@ static int expect (const char *args) { 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]); + 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.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]); - + 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 = proj_trans (T.P, T.dir, ci); - T.b = proj_angular_output (T.P, T.dir)? todeg_coord (co): co; + 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", ci.v[0],ci.v[1],ci.v[2],ci.v[3]); - - /* but there are a few more possible input conventions... */ - if (proj_angular_output (T.P, T.dir)) { - double e = HUGE_VAL; - 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; + 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, co); else - d = proj_xyz_dist (T.b.xyz, T.e.xyz); + d = proj_xyz_dist (co, ce); + if (d > T.tolerance) return expect_message (d, args); another_success (); - return 0; } @@ -791,7 +866,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 +881,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 +894,18 @@ 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, "<begin>") || 0==strcmp (cmnd, "<gie>")) - 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, "ignore")) return ignore (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, "<end>")) return finish_previous_operation (args), level++, 0; - if (0==strcmp (cmnd, "</gie>")) 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; } @@ -934,6 +972,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}, }; @@ -941,7 +980,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 +989,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 +1005,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 +1044,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,111 +1054,324 @@ 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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include <string.h> +#include <ctype.h> + +#include <math.h> +#include <errno.h> + + + +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; + } + + G->next_args = calloc (1, max_record_size); + if (0==G->args) { + free (G->args); + free (G); + return 0; } - *(next) = 0; - return (int) strlen(inp); + + 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 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 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 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 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 <gie>, 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 <gie>-block. Return 1 on success, 0 otherwise. +****************************************************************************************/ + /* Already inside */ + if (G->level % 2) + return 1; + + if (0==locate_tag (G, "<gie>")) + return 0; + + while (0!=strncmp ("<gie>", 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 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 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 <gie> block, locate the next and retry */ + if (0==strcmp (c, "</gie>")) { + 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; +} -char tc32_utm32[] = { +static const char tc32_utm32[] = { " +proj=horner" " +ellps=intl" " +range=500000" @@ -1140,7 +1385,7 @@ char tc32_utm32[] = { }; -char sb_utm32[] = { +static const char sb_utm32[] = { " +proj=horner" " +ellps=intl" " +range=500000" @@ -1171,6 +1416,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); @@ -1186,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; @@ -1239,7 +1485,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"}; + char arg[50] = {"+proj=utm; +zone=32; +ellps=GRS80"}; char buf[40]; /* An utm projection on the GRS80 ellipsoid */ @@ -1274,12 +1520,12 @@ 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; /* Clear any previous error */ - proj_errno_set (P, 0); + proj_errno_reset (P); /* Invalid projection */ a = proj_trans (P, 42, a); @@ -1290,7 +1536,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 +1556,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,13 +1709,15 @@ 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); 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 */ @@ -1479,14 +1727,16 @@ 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; } + 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; @@ -1500,7 +1750,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; @@ -1522,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 */ @@ -1587,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" - " +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; @@ -1604,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; } @@ -1613,17 +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); @@ -1650,34 +1892,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"; @@ -1692,20 +1906,15 @@ 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 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 + 50; + ret = test_time(args5, 1e-6, in5, in5); if (ret) return ret + 50; return 0; } - diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 88d88a97..c9e4d9e6 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 @@ -286,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 + $<INSTALL_INTERFACE:${INCLUDEDIR}>) +endif () if(WIN32) set_target_properties(${PROJ_CORE_TARGET} diff --git a/src/makefile.vc b/src/makefile.vc index ef460719..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 \ @@ -102,27 +101,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) @@ -159,6 +158,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 +167,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/multistresstest.c b/src/multistresstest.c index ddbbb033..e32c7ae4 100644 --- a/src/multistresstest.c +++ b/src/multistresstest.c @@ -32,10 +32,10 @@ #include "proj_api.h" #ifdef _WIN32 - #include <windows.h> + #include <windows.h> #else - #include <pthread.h> - #include <unistd.h> + #include <pthread.h> + #include <unistd.h> #endif #define num_threads 10 @@ -49,12 +49,12 @@ 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[] = { +static TestItem test_list[] = { { "+proj=utm +zone=11 +datum=WGS84", "+proj=latlong +datum=WGS84", @@ -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, @@ -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; @@ -201,11 +201,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 +215,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 +239,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 +255,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 +295,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 +347,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 +373,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 +400,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 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/optargpm.h b/src/optargpm.h index acb96583..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. @@ -183,15 +183,12 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-09-10 * DEALINGS IN THE SOFTWARE. ***********************************************************************/ - -#define PJ_LIB__ -#include <proj.h> +#include <ctype.h> +#include <errno.h> +#include <math.h> #include <stdio.h> #include <stdlib.h> -#include <ctype.h> #include <string.h> -#include <math.h> -#include <errno.h> /**************************************************************************************************/ struct OPTARGS; @@ -417,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)); @@ -442,7 +440,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 +499,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 +516,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 +582,23 @@ 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; + + /* 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 + free_format + 1; + return o; + } + for (/* empty */; i < argc; i++) { if ('-' == argv[i][0]) { free (o); diff --git a/src/pj_datums.c b/src/pj_datums.c index c9655cd6..f084f9cc 100644 --- a/src/pj_datums.c +++ b/src/pj_datums.c @@ -25,17 +25,18 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PJ_DATUMS__ +#include "proj.h" +#define PJ_DATUMS__ #include <projects.h> -/* +/* * The ellipse code must match one from pj_ellps.c. The datum id should - * be kept to 12 characters or less if possible. Use the official OGC - * datum name for the comments if available. + * be kept to 12 characters or less if possible. Use the official OGC + * 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", ""}, @@ -46,7 +47,8 @@ C_NAMESPACE_VAR 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", @@ -65,12 +67,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"}, @@ -86,12 +87,16 @@ C_NAMESPACE_VAR 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} }; 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..4005d1ce 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"}, @@ -13,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"}, @@ -53,7 +57,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_factors.c b/src/pj_factors.c index 31c0e539..17b39c10 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; @@ -40,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 e010f6ec..d238fa4d 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -1,58 +1,261 @@ -/* general forward projection */ -#define PJ_LIB__ -#include <proj.h> -#include <projects.h> -# 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 <errno.h> + +#include "proj_internal.h" +#include "projects.h" + +#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_ANGULAR) { + double t; - /* 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 = (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 (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 (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_geocentric_latitude (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_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); /* 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; + + /* Ensure longitude is in the -pi:pi range */ + if (0==P->over) + coo.lp.lam = adjlon(coo.lp.lam); + + return coo; } - /* Do the transformation */ - xy = (*P->fwd)(lp, P); - if ( proj_errno (P) ) - return err; + + /* We do not support gridshifts on cartesian input */ + if (INPUT_UNITS==PJ_IO_UNITS_CARTESIAN && P->helmert) + return proj_trans (P->helmert, PJ_INV, coo); + return 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) { - xy.x *= P->a; - xy.y *= P->a; + 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_ANGULAR: + if (INPUT_UNITS==PJ_IO_UNITS_ANGULAR) + 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_FWD, coo); /* Go orthometric from geometric */ + if (coo.lp.lam==HUGE_VAL) + return coo; + 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_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) + coo = proj_geocentric_latitude (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 */ - 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 */ + if (P->axisswap) + coo = proj_trans (P->axisswap, PJ_FWD, coo); + + 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 <proj.h> -#include <projects.h> -#include <errno.h> -# 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_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_geocent.c b/src/pj_geocent.c index eca62080..3d771c43 100644 --- a/src/pj_geocent.c +++ b/src/pj_geocent.c @@ -48,12 +48,14 @@ 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; P->inv = inverse; P->fwd = forward; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_CARTESIAN; return P; } diff --git a/src/pj_gridinfo.c b/src/pj_gridinfo.c index 9b9a8d82..b855980b 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 ) @@ -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); diff --git a/src/pj_init.c b/src/pj_init.c index 2d588ee4..78486480 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -27,320 +27,355 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ + + #define PJ_LIB__ #include <geodesic.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <ctype.h> +#include <proj.h> #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 "<section>" indicator, and copy the rest of the line over */ + strcpy (buffer, line + strlen (section) + 2); + + /* Copy the remaining lines of the section to buffer */ + for (;;) { + char *end_i_cator; + size_t next_length, buffer_length; + /* 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; } - /* Is this our target? */ - else if( *next_char == '<' ) - { - /* terminate processing target on the next block definition */ - if (in_target) - break; + /* End of file? - done! */ + if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) + break; - 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++; + /* 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); + if (0==b) { + pj_dealloc (buffer); + buffer = 0; + break; } + strcpy (b, buffer); + current_buffer_size *= 2; + pj_dealloc (buffer); + buffer = b; } - else if (in_target) - { - const char *start_of_word = next_char; - int word_len = 0; + buffer[buffer_length] = ' '; + strcpy (buffer + buffer_length + 1, line); + } - if (*start_of_word == '+') - { - start_of_word++; - next_char++; - } + 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; +} - /* capture parameter */ - while ( *next_char && !isspace(*next_char) ) - { - next_char++; - word_len++; - } - strncpy(sword+1, start_of_word, word_len); - sword[word_len+1] = '\0'; - /* do not override existing parameter value of same name */ - if (!pj_param(ctx, *start, sword).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); + 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; - /* don't default ellipse if datum, ellps or any earth model information is set */ - if (0==strncmp(sword,"tellps=", 7)) { - int n = 0; + /* We found it in file - now insert into the cache, before returning */ + pj_insert_initcache (xkey, init_items); + return init_items; +} - 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; - if (0==n) - next = next->next = pj_mkparam(sword+1); - } - else - next = next->next = pj_mkparam(sword+1); - } +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; - } - else - { - /* skip past word */ - while( *next_char && !isspace(*next_char) ) { - next_char++; - } + if (0==start) + return 0; + + if (strlen(key) > ID_TAG_MAX) + return 0; + /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */ + /* or implicitly, because we are initializing a pipeline) */ + if (pj_param_exists (start, "no_defs")) + return start; + proj = pj_param_exists (start, "proj"); + if (0==proj) + return start; + if (strlen (proj->param) < 6) + return start; + if (0==strcmp ("pipeline", proj->param + 5)) + return start; + + err = pj_ctx_get_errno (ctx); + pj_ctx_set_errno (ctx, 0); + + /* Locate end of start-list */ + for (last = start; last->next; last = last->next); + + strcpy (keystring, "proj_def.dat:"); + strcat (keystring, key); + defaults = get_init (ctx, keystring); + + /* Defaults are optional - so we don't care if we cannot open the file */ + pj_ctx_set_errno (ctx, err); + + if (!defaults) + return last; + + /* Loop over all default items */ + for (next = defaults; next; next = next->next) { + + /* Don't override existing parameter value of same name */ + if (pj_param_exists (start, next->param)) + continue; + + /* Don't default ellipse if datum, ellps or any ellipsoid information is set */ + if (0==strncmp(next->param,"ellps=", 6)) { + if (pj_param_exists (start, "datum")) continue; + if (pj_param_exists (start, "ellps")) continue; + if (pj_param_exists (start, "a")) continue; + if (pj_param_exists (start, "b")) continue; + if (pj_param_exists (start, "rf")) continue; + if (pj_param_exists (start, "f")) continue; + if (pj_param_exists (start, "e")) continue; + if (pj_param_exists (start, "es")) continue; } - } - if (errno == 25) - errno = 0; + /* If we're here, it's OK to append the current default item */ + last = last->next = pj_mkparam(next->param); + } + last->next = 0; - free(state); - return next; + pj_dealloc_params (ctx, defaults, 0); + return last; } -/************************************************************************/ -/* get_defaults() */ -/************************************************************************/ -static paralist *get_defaults(projCtx ctx, paralist **start, paralist *next, char *name) { - PAFile fid; +/*****************************************************************************/ +paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) { +/****************************************************************************** +Append expansion of <key> to the paralist <init>. The expansion is appended, +rather than inserted at <init>'s place, since <init> may contain +overrides to the expansion. These must take precedence, and hence come first +in the expanded list. - 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 (errno) - errno = 0; /* don't care if can't open file */ - ctx->last_errno = 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. - return next; -} +The expression 'init=foo:bar ellps=intl' will then expand to: -/************************************************************************/ -/* 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; + 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80', - (void)strncpy(fname, name, sizeof(fname)-2); - fname[sizeof(fname)-2] = '\0'; +where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence, +turning the expansion into an UTM projection on the Hayford ellipsoid. - /* - ** Search for file/key pair in cache - */ +Note that 'init=foo:bar' stays in the list. It is ignored after expansion. - 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; - } +******************************************************************************/ + paralist *last; + paralist *expn; - /* - ** 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; } + /* Nowhere to start? */ + if (0==init) + return 0; - if ( (fid = pj_open_lib(ctx,fname, "rt")) != NULL) - next = get_opt(ctx, start, fid, opt, next, found_def); - else - return NULL; + expn = get_init(ctx, init->param); - pj_ctx_fclose(ctx, fid); - if (errno == 25) - errno = 0; /* unknown problem with some sys errno<-25 */ + /* Nothing in expansion? */ + if (0==expn) + 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 ); + /* Locate the end of the list */ + for (last = init; last && last->next; last = last->next); - return next; + /* Then append and return */ + last->next = expn; + return init; } -paralist * pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) { - return get_init(ctx, start, next, name, found_def); -} + /************************************************************************/ /* 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 +469,24 @@ pj_init(int argc, char **argv) { return pj_init_ctx( pj_get_default_ctx(), argc, argv ); } + +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 (PJ_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; + PJ_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); @@ -466,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; @@ -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_locate_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,21 +587,30 @@ 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)); - if (PIN->need_ellps) { - int ret = pj_ellipsoid (PIN); - if (0 != ret) { + err = pj_ellipsoid (PIN); + + if (err) { + /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */ + if (PIN->need_ellps) { pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); return pj_default_destructor (PIN, proj_errno(PIN)); } - 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); + else { + if (PJD_ERR_MAJOR_AXIS_NOT_GIVEN==proj_errno (PIN)) + proj_errno_reset (PIN); + PIN->f = 1.0/298.257223563; + PIN->a_orig = PIN->a = 6378137.0; + PIN->es_orig = PIN->es = PIN->f*(2-PIN->f); + } } + PIN->a_orig = PIN->a; + PIN->es_orig = PIN->es; + if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es)) + return pj_default_destructor (PIN, PJD_ERR_ECCENTRICITY_IS_ONE); /* Now that we have ellipse information check for WGS84 datum */ if( PIN->datum_type == PJD_3PARAM @@ -566,18 +623,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,10 +645,10 @@ 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"; + 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); @@ -601,23 +658,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 +684,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) ; @@ -640,9 +697,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); @@ -655,7 +712,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 +732,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 +765,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)) { @@ -718,24 +775,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_internal.c b/src/pj_internal.c index 8a5d2d15..4dbcfbd4 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,28 +28,28 @@ * 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 <geodesic.h> - +#include <ctype.h> #include <stddef.h> #include <stdarg.h> #include <errno.h> +#include <geodesic.h> +#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; - 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; } @@ -61,39 +61,72 @@ 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 (); } - - +/**************************************************************************************/ +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 */ @@ -104,6 +137,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()); @@ -114,60 +148,216 @@ void proj_context_inherit (PJ *parent, PJ *child) { -size_t pj_strlcpy(char *dst, const char *src, size_t siz) { -/******************************************************************* - 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. +/*****************************************************************************/ +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; +} - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> - * - * 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://www.i-pi.com/Training/EthicalHacking/Solutions/strlcpy.c - -********************************************************************/ - register char *d = dst; - register const char *s = src; - register size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); + + +/*****************************************************************************/ +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]) && ws) + c[j] = ' '; + if ((i==0) && ('+'==c[j])) + 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; +} + + - /* 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++) - ; +/*****************************************************************************/ +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; +} + + - return(s - src - 1); /* count does not include NUL */ +/*****************************************************************************/ +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; } -/* stuff below is *not* considered API, and will be moved to an "internal plumbing toolset" */ +/*****************************************************************************/ +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 +366,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 +393,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_inv.c b/src/pj_inv.c index 68a5595b..4ea88b69 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -1,60 +1,259 @@ -/* general inverse projection */ -#define PJ_LIB__ -#include <proj.h> -#include <projects.h> +/****************************************************************************** + * 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 <errno.h> -# 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" + +#define INPUT_UNITS P->right +#define OUTPUT_UNITS P->left + +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_ANGULAR) { + 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_geocentric_latitude (P, PJ_INV, coo); - /* cannot const-initialize this due to MSVC's broken (non const) HUGE_VAL */ - err.lam = err.phi = HUGE_VAL; + /* Distance from central meridian, taking system zero meridian into account */ + coo.lp.lam = (coo.lp.lam + P->from_greenwich) - P->lam0; - if (0==P->inv) - return err; + /* Ensure longitude is in the -pi:pi range */ + if (0==P->over) + coo.lp.lam = adjlon(coo.lp.lam); - /* 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; + 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_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); /* Go geometric from orthometric */ + return coo; } - last_errno = proj_errno_reset (P); + /* Handle remaining possible input types */ + switch (INPUT_UNITS) { + case PJ_IO_UNITS_WHATEVER: + return coo; /* de-scale and de-offset */ - xy.x = (xy.x * P->to_meter - P->x0); - xy.y = (xy.y * P->to_meter - P->y0); + 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_ANGULAR not handled */ + default: + break; + } + + /* Should not happen, so we could return pj_coord_err here */ + return 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 (); + } - /* 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) { - xy.x *= P->ra; - xy.y *= P->ra; + if (OUTPUT_UNITS==PJ_IO_UNITS_ANGULAR) { + + 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; + + /* 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); /* Go geometric from orthometric */ + 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_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 */ + if (P->geoc) + coo = proj_geocentric_latitude (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; +} - /* Do inverse transformation */ - lp = (*P->inv) (xy, P); - if (P->ctx->last_errno) - return err; - if (P->left==PJ_IO_UNITS_RADIANS) { - /* reduce from del lp.lam */ - lp.lam += P->lam0; - /* adjust longitude to central meridian */ - if (!P->over) - lp.lam = adjlon(lp.lam); +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 <proj.h> -#include <projects.h> -#include <errno.h> -# 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_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_malloc.c b/src/pj_malloc.c index c9275074..127e76ee 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 <proj.h> #include "projects.h" #include <errno.h> @@ -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. @@ -161,6 +162,32 @@ void *pj_dealloc_params (projCtx 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, proj_errno(P)); +} + + + + /*****************************************************************************/ void *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ /***************************************************************************** @@ -194,6 +221,15 @@ 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); + 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/pj_open_lib.c b/src/pj_open_lib.c index 08532beb..054853c6 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) { @@ -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_param.c b/src/pj_param.c index ee952eca..133f3ea6 100644 --- a/src/pj_param.c +++ b/src/pj_param.c @@ -1,20 +1,89 @@ /* put parameters in linked list and retrieve */ -#include <projects.h> +#include <ctype.h> #include <stdio.h> #include <string.h> +#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 <str> */ +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. + + 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 + 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)) { + next->used = 1; + return next; + } + if (0==strcmp (parameter, "step")) + return 0; + } + + return 0; } @@ -33,78 +102,82 @@ paralist *pj_mkparam(char *str) { /* `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) { - - 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: -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; +PROJVALUE pj_param (projCtx ctx, paralist *pl, const char *opt) { + + int type; + unsigned l; + PROJVALUE value = {0}; + + if ( ctx == NULL ) + ctx = pj_get_default_ctx(); + + type = *opt++; + + 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; + return value; + } + + /* Not found */ + if (0==pl) { + switch (type) { + case 'b': case 'i': + value.i = 0; + return value; + case 'd': case 'r': + value.f = 0.; + 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: + pj_ctx_set_errno (ctx, PJD_ERR_INVALID_BOOLEAN_PARAM); + value.i = 0; + break; + } + break; + } + return value; } 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 <projects.h> -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() 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 <errno.h> #include <string.h> - 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_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/pj_transform.c b/src/pj_transform.c index 21861331..fc0a3241 100644 --- a/src/pj_transform.c +++ b/src/pj_transform.c @@ -117,15 +117,6 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, } /* -------------------------------------------------------------------- */ -/* 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. */ /* -------------------------------------------------------------------- */ if( srcdefn->is_geocent ) @@ -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; } } } @@ -454,15 +447,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. */ /* -------------------------------------------------------------------- */ @@ -764,17 +748,32 @@ 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 ( srcnadgrids != NULL && + 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 ( dstnadgrids != NULL && + strcmp("@null", dstnadgrids) && strcmp("null", dstnadgrids) ) { + dst_a = SRS_WGS84_SEMIMAJOR; + dst_es = SRS_WGS84_ESQUARED; + } } /* ==================================================================== */ 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 <projects.h> + /* 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; @@ -1,4 +1,5 @@ /* <<<< Cartographic projection filter program >>>> */ +#include "proj.h" #include "projects.h" #include <stdio.h> #include <stdlib.h> @@ -10,7 +11,7 @@ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__) # include <fcntl.h> # include <io.h> -# 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 */ @@ -40,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"; @@ -55,7 +62,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 +75,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 +101,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 +122,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; + 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) { - data.v *= RAD_TO_DEG; - data.u *= RAD_TO_DEG; + if (Proj->right == PJ_IO_UNITS_ANGULAR) { + 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 +188,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 +232,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; + if (!inverse && Proj->right == PJ_IO_UNITS_ANGULAR) { + 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 +284,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.); @@ -352,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 ) @@ -373,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) @@ -489,9 +495,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); diff --git a/src/proj.def b/src/proj.def index 887f4719..d6c84dc3 100644 --- a/src/proj.def +++ b/src/proj.def @@ -90,62 +90,68 @@ EXPORTS geod_polygon_clear @88 pj_find_file @89 + 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 @90 - proj_create_argv @91 - proj_create_crs_to_crs @92 - proj_destroy @93 + proj_create @96 + proj_create_argv @97 + proj_create_crs_to_crs @98 + proj_destroy @99 - proj_trans @94 - proj_trans_array @95 - proj_trans_generic @96 - proj_roundtrip @97 + proj_trans @100 + proj_trans_array @101 + proj_trans_generic @102 + proj_roundtrip @103 - proj_coord @98 - proj_coord_error @99 + proj_coord @104 + proj_coord_error @105 - proj_errno @100 - proj_errno_set @101 - proj_errno_reset @102 - proj_errno_restore @103 - proj_context_errno_set @104 + proj_errno @106 + proj_errno_set @107 + proj_errno_reset @108 + proj_errno_restore @109 + proj_context_errno_set @110 - proj_context_create @105 - proj_context_set @106 - proj_context_inherit @107 - proj_context_destroy @108 + proj_context_create @111 + proj_context_set @112 + proj_context_inherit @113 + proj_context_destroy @114 - proj_lp_dist @109 - proj_lpz_dist @110 - proj_xy_dist @111 - proj_xyz_dist @112 + proj_lp_dist @115 + proj_lpz_dist @116 + proj_xy_dist @117 + proj_xyz_dist @118 - proj_log_level @113 - proj_log_func @114 - proj_log_error @115 - proj_log_debug @116 - proj_log_trace @117 + proj_log_level @119 + proj_log_func @120 + proj_log_error @121 + proj_log_debug @122 + proj_log_trace @123 - proj_info @118 - proj_pj_info @119 - proj_grid_info @120 - proj_init_info @121 + proj_info @124 + proj_pj_info @125 + proj_grid_info @126 + proj_init_info @127 - proj_torad @122 - proj_todeg @123 - proj_geoc_lat @124 - proj_rtodms @125 - proj_dmstor @126 + proj_torad @128 + proj_todeg @129 + proj_geocentric_latitude @130 + proj_rtodms @131 + proj_dmstor @132 - proj_factors @127 + proj_factors @133 - proj_list_operations @128 - proj_list_ellps @129 - proj_list_units @130 - proj_list_prime_meridians @131 + proj_list_operations @134 + proj_list_ellps @135 + proj_list_units @136 + proj_list_prime_meridians @137 - proj_angular_input @132 - proj_angular_output @133 + proj_angular_input @138 + proj_angular_output @139 - pj_ellipsoid @134 - pj_calc_ellipsoid_params @135 + proj_geod @140 + proj_context_errno @141 @@ -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 <math.h> For M_PI */ + #include <stddef.h> /* 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 */ @@ -167,6 +156,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; @@ -206,15 +198,17 @@ 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 */ +typedef struct { double s, a1, a2; } PJ_GEOD; /* Geodesic length, fwd azi, rev azi */ -/* Classic proj.4 pair/triplet types */ -typedef struct { double u, v; } UV; -typedef struct { double x, y; } XY; -typedef struct { double lam, phi; } LP; +/* 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; -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 */ @@ -223,31 +217,35 @@ union PJ_COORD { PJ_XYZT xyzt; PJ_UVWT uvwt; PJ_LPZT lpzt; + PJ_GEOD geod; PJ_OPK opk; - XYZ xyz; - UVW uvw; - LPZ lpz; - XY xy; - UV uv; - LP lp; + PJ_ENU enu; + PJ_XYZ xyz; + PJ_UVW uvw; + PJ_LPZ lpz; + PJ_XY xy; + PJ_UV uv; + PJ_LP lp; }; 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. */ }; @@ -256,8 +254,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 */ }; @@ -329,29 +327,34 @@ 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_COORD a, PJ_COORD 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_COORD a, PJ_COORD b); /* Euclidean distance between two points with linear 2D coordinates */ -double proj_xy_dist (XY a, 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 (XYZ a, 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 */ +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); 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_COORD 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); @@ -370,7 +373,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 5f4bf334..fb20978b 100644 --- a/src/proj_4D_api.c +++ b/src/proj_4D_api.c @@ -28,7 +28,6 @@ *****************************************************************************/ #include <stddef.h> #include <errno.h> -#include <ctype.h> #include <proj.h> #include "proj_internal.h" #include "projects.h" @@ -53,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; } /*****************************************************************************/ @@ -68,30 +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; - return hypot (proj_lp_dist (P, aa.lp, bb.lp), a.z - b.z); +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, 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); } @@ -99,7 +115,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; @@ -110,29 +126,36 @@ 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, t); - return proj_xyz_dist (org.xyz, u.xyz); + return proj_xyz_dist (org, t); } -/* 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) @@ -334,13 +357,13 @@ 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) 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 +372,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; @@ -372,6 +395,131 @@ 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. + +Returns 1 on success, 0 on failure +**************************************************************************************/ + PJ *Q; + paralist *p; + if (0==P) + return 0; + + /* Don't recurse when calling proj_create (which calls us back) */ + if (pj_param_exists (P->params, "break_cs2cs_recursion")) + return 1; + + /* Swap axes? */ + p = pj_param_exists (P->params, "axis"); + + /* Don't axisswap if data are already in "enu" order */ + if (p && (0!=strcmp ("enu", p->param))) { + char *def = malloc (100+strlen(P->axis)); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); + Q = proj_create (P->ctx, def); + free (def); + if (0==Q) + return 0; + P->axisswap = skip_prep_fin(Q); + } + + /* Geoid grid(s) given? */ + p = pj_param_exists (P->params, "geoidgrids"); + if (p && strlen (p->param) > strlen ("geoidgrids=")) { + char *gridnames = p->param + strlen ("geoidgrids="); + char *def = malloc (100+strlen(gridnames)); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames); + Q = proj_create (P->ctx, def); + free (def); + if (0==Q) + return 0; + P->vgridshift = skip_prep_fin(Q); + } + + /* Datum shift grid(s) given? */ + p = pj_param_exists (P->params, "nadgrids"); + if (p && strlen (p->param) > strlen ("nadgrids=")) { + char *gridnames = p->param + strlen ("nadgrids="); + char *def = malloc (100+strlen(gridnames)); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames); + Q = proj_create (P->ctx, def); + free (def); + if (0==Q) + return 0; + P->hgridshift = skip_prep_fin(Q); + } + + /* We ignore helmert if we have grid shift */ + p = P->hgridshift ? 0 : pj_param_exists (P->params, "towgs84"); + while (p) { + char *def; + char *s = p->param; + double *d = P->datum_params; + size_t n = strlen (s); + + /* We ignore null helmert shifts (common in auto-translated resource files, e.g. epsg) */ + if (0==d[0] && 0==d[1] && 0==d[2] && 0==d[3] && 0==d[4] && 0==d[5] && 0==d[6]) + break; + + if (n <= 8) /* 8==strlen ("towgs84=") */ + return 0; + + def = malloc (100+n); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=helmert %s transpose", s); + Q = proj_create (P->ctx, def); + pj_inherit_ellipsoid_def (P, Q); + free (def); + if (0==Q) + return 0; + P->helmert = skip_prep_fin (Q); + + break; + } + + /* We also need cartesian/geographical transformations if we are working in */ + /* geocentric/cartesian space or we need to do a Helmert transform. */ + if (P->is_geocent || P->helmert) { + 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; + P->cart = skip_prep_fin (Q); + + sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); + Q = proj_create (P->ctx, def); + if (0==Q) + return 0; + P->cart_wgs84 = skip_prep_fin (Q); + } + + return 1; +} @@ -386,80 +534,40 @@ 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; - int argc, i, j, last, n; + PJ *P; + char *args, **argv; + size_t argc, n; + int ret; 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); + + /* Support cs2cs-style modifiers */ + ret = pj_cs2cs_emulation_setup (P); + if (0==ret) + return proj_destroy (P); + return P; } @@ -474,11 +582,23 @@ 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; } @@ -531,11 +651,26 @@ 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) { /****************************************************************************** Set context-errno, bubble it up to the thread local errno, return err @@ -599,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; } @@ -622,70 +758,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; + } - Returns PJ_INFO struct. Searchpath member of the struct is truncated to 512 - characters. + /* Only append a semicolon if something's already there */ + if (0 != buflen) + strcat (buf, ";"); + strcat (buf, app); + return buf; +} +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 **paths; - char *tmpstr; - int i, n; - size_t len = 0; + const char * const *paths; + 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)); - } - - 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)); - } - } + buf = path_append (buf, getenv ("HOME"), &buf_size); + buf = path_append (buf, getenv ("PROJ_LIB"), &buf_size); - paths = proj_get_searchpath(); - n = proj_get_path_count(); + paths = proj_get_searchpath (); + n = (size_t) proj_get_path_count (); - for (i=0; i<n; i++) { - if (strlen(info.searchpath)+strlen(paths[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; } @@ -696,39 +875,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); - - /* 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); + 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; } @@ -738,49 +919,53 @@ 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; } + + /*****************************************************************************/ PJ_INIT_INFO proj_init_info(const char *initname){ /****************************************************************************** @@ -790,75 +975,77 @@ 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; + 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); 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)); - } + 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; } /*****************************************************************************/ -PJ_FACTORS proj_factors(PJ *P, LP lp) { +PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { /****************************************************************************** Cartographic characteristics at point 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 (pj_factors(lp, P, 0.0, &f)) + if (0==P) + return factors; + + if (pj_factors(lp.lp, P, 0.0, &f)) return factors; factors.meridional_scale = f.h; @@ -872,23 +1059,12 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) { factors.tissot_semimajor = f.a; factors.tissot_semiminor = f.b; - return factors; -} + /* 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; - -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(); + return factors; } diff --git a/src/proj_api.h b/src/proj_api.h index 597a2589..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; @@ -155,6 +161,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); 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 <dlfcn.h> header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the <inttypes.h> 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 <memory.h> header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the <stdint.h> header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H - -/* Define to 1 if you have the <strings.h> header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H - -/* Define to 1 if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H - -/* Define to 1 if you have the <sys/types.h> header file. */ -#define HAVE_SYS_TYPES_H - -/* Define to 1 if you have the <unistd.h> 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/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 <errno.h> diff --git a/src/proj_internal.h b/src/proj_internal.h index 4e70e690..071510c7 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -57,12 +57,18 @@ 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 */ +/* Maximum latitudinal overshoot accepted */ +#define PJ_EPS_LAT 1e-12 + + +/* 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_ANGULAR = 4 /* Radians */ }; enum pj_io_units pj_left (PJ *P); enum pj_io_units pj_right (PJ *P); @@ -78,12 +84,16 @@ 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_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); -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 { @@ -105,18 +115,23 @@ 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); +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); -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); - #ifdef __cplusplus } #endif diff --git a/src/proj_mdist.c b/src/proj_mdist.c index 244cf199..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 <projects.h> #define MAX_ITER 20 #define TOL 1e-14 @@ -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 8c0f81fa..25ff82a4 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 <wce_stdlib.h> -# include <wce_stdio.h> -# 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 @@ -165,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; @@ -177,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 */ @@ -192,9 +189,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_ANGULAR = 4 /* Radians */ }; #endif #ifndef PROJ_H @@ -219,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 { @@ -237,6 +267,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; @@ -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; /************************************************************************************* @@ -337,11 +368,24 @@ 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; + /* 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; + /************************************************************************************* @@ -349,7 +393,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) */ @@ -387,7 +431,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; @@ -590,7 +634,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) \ \ @@ -608,7 +652,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; \ } \ @@ -683,9 +727,11 @@ 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 *); int pj_datum_set(projCtx,paralist *, PJ *); int pj_prime_meridian_set(paralist *, PJ *); @@ -694,7 +740,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); @@ -709,8 +756,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 <assert.h> #include <unistd.h> -volatile int run = 0; -volatile int started = 0; +static volatile int run = 0; +static volatile int started = 0; static void* thread_main(void* unused) { |
