diff options
Diffstat (limited to 'src/transform.cpp')
| -rw-r--r-- | src/transform.cpp | 1047 |
1 files changed, 1047 insertions, 0 deletions
diff --git a/src/transform.cpp b/src/transform.cpp new file mode 100644 index 00000000..433fc017 --- /dev/null +++ b/src/transform.cpp @@ -0,0 +1,1047 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Perform overall coordinate system to coordinate system + * transformations (pj_transform() function) including reprojection + * and datum shifting. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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 <math.h> +#include <string.h> + +#include "proj.h" +#include "projects.h" +#include "geocent.h" + +static int adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, + long point_count, int point_offset, + double *x, double *y, double *z ); + +#ifndef SRS_WGS84_SEMIMAJOR +#define SRS_WGS84_SEMIMAJOR 6378137.0 +#endif + +#ifndef SRS_WGS84_ESQUARED +#define SRS_WGS84_ESQUARED 0.0066943799901413165 +#endif + +#define Dx_BF (defn->datum_params[0]) +#define Dy_BF (defn->datum_params[1]) +#define Dz_BF (defn->datum_params[2]) +#define Rx_BF (defn->datum_params[3]) +#define Ry_BF (defn->datum_params[4]) +#define Rz_BF (defn->datum_params[5]) +#define M_BF (defn->datum_params[6]) + +/* +** This table is intended to indicate for any given error code +** whether that error will occur for all locations (ie. +** it is a problem with the coordinate system as a whole) in which case the +** value would be 0, or if the problem is with the point being transformed +** in which case the value is 1. +** +** At some point we might want to move this array in with the error message +** list or something, but while experimenting with it this should be fine. +** +** +** NOTE (2017-10-01): Non-transient errors really should have resulted in a +** PJ==0 during initialization, and hence should be handled at the level +** before calling pj_transform. The only obvious example of the contrary +** appears to be the PJD_ERR_GRID_AREA case, which may also be taken to +** mean "no grids available" +** +** +*/ + +static const int transient_error[60] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, + /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 40 to 49 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + /* 50 to 59 */ 1, 0, 1, 0, 1, 1, 1, 1, 0, 0 }; + + +/* -------------------------------------------------------------------- */ +/* Read transient_error[] in a safe way. */ +/* -------------------------------------------------------------------- */ +static int get_transient_error_value(int pos_index) +{ + const int array_size = + (int)(sizeof(transient_error) / sizeof(transient_error[0])); + if( pos_index < 0 || pos_index >= array_size ) { + return 0; + } + return transient_error[pos_index]; +} + + +/* -------------------------------------------------------------------- */ +/* Transform unusual input coordinate axis orientation to */ +/* standard form if needed. */ +/* -------------------------------------------------------------------- */ +static int adjust_axes (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { + /* Nothing to do? */ + if (0==strcmp(P->axis,"enu")) + return 0; + + return adjust_axis( P->ctx, P->axis, + dir==PJ_FWD ? 1: 0, n, dist, x, y, z ); +} + + + +/* ----------------------------------------------------------------------- */ +/* Transform geographic (lat/long) source coordinates to */ +/* cartesian ("geocentric"), if needed */ +/* ----------------------------------------------------------------------- */ +static int geographic_to_cartesian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { + int res; + long i; + double fac = P->to_meter; + + /* Nothing to do? */ + if (!P->is_geocent) + return 0; + + if ( z == nullptr ) { + pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); + return PJD_ERR_GEOCENTRIC; + } + + if (PJ_FWD==dir) { + fac = P->fr_meter; + res = pj_geodetic_to_geocentric( P->a_orig, P->es_orig, n, dist, x, y, z ); + if (res) + return res; + } + + if (fac != 1.0) { + for( i = 0; i < n; i++ ) { + if( x[dist*i] != HUGE_VAL ) { + x[dist*i] *= fac; + y[dist*i] *= fac; + z[dist*i] *= fac; + } + } + } + + if (PJ_FWD==dir) + return 0; + return pj_geocentric_to_geodetic( + P->a_orig, P->es_orig, + n, dist, + x, y, z + ); +} + + + + + + + + + + +/* -------------------------------------------------------------------- */ +/* Transform destination points to projection coordinates, if */ +/* desired. */ +/* */ +/* Ought to fold this into projected_to_geographic */ +/* -------------------------------------------------------------------- */ +static int geographic_to_projected (PJ *P, long n, int dist, double *x, double *y, double *z) { + long i; + + /* Nothing to do? */ + if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) + return 0; + if (P->is_geocent) + return 0; + + if(P->fwd3d != nullptr && !(z == nullptr && P->is_latlong)) + { + /* Three dimensions must be defined */ + if ( z == nullptr) + { + pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); + return PJD_ERR_GEOCENTRIC; + } + + for( i = 0; i < n; i++ ) + { + XYZ projected_loc; + LPZ geodetic_loc; + + geodetic_loc.lam = x[dist*i]; + geodetic_loc.phi = y[dist*i]; + geodetic_loc.z = z[dist*i]; + + if (geodetic_loc.lam == HUGE_VAL) + continue; + + proj_errno_reset( P ); + projected_loc = pj_fwd3d( geodetic_loc, P); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + projected_loc.x = HUGE_VAL; + projected_loc.y = HUGE_VAL; + projected_loc.z = HUGE_VAL; + } + } + + x[dist*i] = projected_loc.x; + y[dist*i] = projected_loc.y; + z[dist*i] = projected_loc.z; + } + return 0; + } + + for( i = 0; i <n; i++ ) + { + XY projected_loc; + LP geodetic_loc; + + geodetic_loc.lam = x[dist*i]; + geodetic_loc.phi = y[dist*i]; + + if( geodetic_loc.lam == HUGE_VAL ) + continue; + + proj_errno_reset( P ); + projected_loc = pj_fwd( geodetic_loc, P ); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + projected_loc.x = HUGE_VAL; + projected_loc.y = HUGE_VAL; + } + } + + x[dist*i] = projected_loc.x; + y[dist*i] = projected_loc.y; + } + return 0; +} + + + + + +/* ----------------------------------------------------------------------- */ +/* Transform projected source coordinates to lat/long, if needed */ +/* ----------------------------------------------------------------------- */ +static int projected_to_geographic (PJ *P, long n, int dist, double *x, double *y, double *z) { + long i; + + /* Nothing to do? */ + if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) + return 0; + if (P->is_geocent) + return 0; + + /* Check first if projection is invertible. */ + if( (P->inv3d == nullptr) && (P->inv == nullptr)) + { + pj_ctx_set_errno(pj_get_ctx(P), PJD_ERR_NON_CONV_INV_MERI_DIST); + pj_log( pj_get_ctx(P), PJ_LOG_ERROR, + "pj_transform(): source projection not invertable" ); + return PJD_ERR_NON_CONV_INV_MERI_DIST; + } + + /* If invertible - First try inv3d if defined */ + if (P->inv3d != nullptr && !(z == nullptr && P->is_latlong)) + { + /* Three dimensions must be defined */ + if ( z == nullptr) + { + pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); + return PJD_ERR_GEOCENTRIC; + } + + for (i=0; i < n; i++) + { + XYZ projected_loc; + LPZ geodetic_loc; + + projected_loc.x = x[dist*i]; + projected_loc.y = y[dist*i]; + projected_loc.z = z[dist*i]; + + if (projected_loc.x == HUGE_VAL) + continue; + + proj_errno_reset( P ); + geodetic_loc = pj_inv3d(projected_loc, P); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + geodetic_loc.lam = HUGE_VAL; + geodetic_loc.phi = HUGE_VAL; + geodetic_loc.z = HUGE_VAL; + } + } + + x[dist*i] = geodetic_loc.lam; + y[dist*i] = geodetic_loc.phi; + z[dist*i] = geodetic_loc.z; + + } + return 0; + } + + /* Fallback to the original PROJ.4 API 2d inversion - inv */ + for( i = 0; i < n; i++ ) { + XY projected_loc; + LP geodetic_loc; + + projected_loc.x = x[dist*i]; + projected_loc.y = y[dist*i]; + + if( projected_loc.x == HUGE_VAL ) + continue; + + proj_errno_reset( P ); + geodetic_loc = pj_inv( projected_loc, P ); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + geodetic_loc.lam = HUGE_VAL; + geodetic_loc.phi = HUGE_VAL; + } + } + + x[dist*i] = geodetic_loc.lam; + y[dist*i] = geodetic_loc.phi; + } + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Adjust for the prime meridian if needed. */ +/* -------------------------------------------------------------------- */ +static int prime_meridian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x) { + int i; + double pm = P->from_greenwich; + + /* Nothing to do? */ + if (pm==0.0) + return 0; + if (!(P->is_geocent || P->is_latlong)) + return 0; + + if (dir==PJ_FWD) + pm = -pm; + + for (i = 0; i < n; i++) + if (x[dist*i] != HUGE_VAL) + x[dist*i] += pm; + + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Adjust for vertical scale factor if needed */ +/* -------------------------------------------------------------------- */ +static int height_unit (PJ *P, PJ_DIRECTION dir, long n, int dist, double *z) { + int i; + double fac = P->vto_meter; + + if (PJ_FWD==dir) + fac = P->vfr_meter; + + /* Nothing to do? */ + if (fac==1.0) + return 0; + if (nullptr==z) + return 0; + if (P->is_latlong) + return 0; /* done in pj_inv3d() / pj_fwd3d() */ + + for (i = 0; i < n; i++) + if (z[dist*i] != HUGE_VAL ) + z[dist*i] *= fac; + + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Transform to ellipsoidal heights if needed */ +/* -------------------------------------------------------------------- */ +static int geometric_to_orthometric (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { + int err; + if (0==P->has_geoid_vgrids) + return 0; + if (z==nullptr) + return PJD_ERR_GEOCENTRIC; + err = pj_apply_vgridshift (P, "sgeoidgrids", + &(P->vgridlist_geoid), + &(P->vgridlist_geoid_count), + dir==PJ_FWD ? 1 : 0, n, dist, x, y, z ); + if (err) + return pj_ctx_get_errno(P->ctx); + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Convert datums if needed, and possible. */ +/* -------------------------------------------------------------------- */ +static int datum_transform (PJ *P, PJ *Q, long n, int dist, double *x, double *y, double *z) { + if (0==pj_datum_transform (P, Q, n, dist, x, y, z)) + return 0; + if (P->ctx->last_errno) + return P->ctx->last_errno; + return Q->ctx->last_errno; +} + + + + + +/* -------------------------------------------------------------------- */ +/* If a wrapping center other than 0 is provided, rewrap around */ +/* the suggested center (for latlong coordinate systems only). */ +/* -------------------------------------------------------------------- */ +static int long_wrap (PJ *P, long n, int dist, double *x) { + long i; + + /* Nothing to do? */ + if (P->is_geocent) + return 0; + if (!P->is_long_wrap_set) + return 0; + if (!P->is_latlong) + return 0; + + for (i = 0; i < n; i++ ) { + double val = x[dist*i]; + if (val == HUGE_VAL) + continue; + + /* Get fast in ] -2 PI, 2 PI [ range */ + val = fmod(val, M_TWOPI); + while( val < P->long_wrap_center - M_PI ) + val += M_TWOPI; + while( val > P->long_wrap_center + M_PI ) + val -= M_TWOPI; + x[dist*i] = val; + } + return 0; +} + + + +/************************************************************************/ +/* pj_transform() */ +/* */ +/* Currently this function doesn't recognise if two projections */ +/* are identical (to short circuit reprojection) because it is */ +/* difficult to compare PJ structures (since there are some */ +/* projection specific components). */ +/************************************************************************/ + +int pj_transform( + PJ *src, PJ *dst, + long point_count, int point_offset, + double *x, double *y, double *z +){ + int err; + + src->ctx->last_errno = 0; + dst->ctx->last_errno = 0; + + if( point_offset == 0 ) + point_offset = 1; + + /* Bring input to "normal form": longitude, latitude, ellipsoidal height */ + + err = adjust_axes (src, PJ_INV, point_count, point_offset, x, y, z); + if (err) + return err; + err = geographic_to_cartesian (src, PJ_INV, point_count, point_offset, x, y, z); + if (err) + return err; + err = projected_to_geographic (src, point_count, point_offset, x, y, z); + if (err) + return err; + err = prime_meridian (src, PJ_INV, point_count, point_offset, x); + if (err) + return err; + err = height_unit (src, PJ_INV, point_count, point_offset, z); + if (err) + return err; + err = geometric_to_orthometric (src, PJ_INV, point_count, point_offset, x, y, z); + if (err) + return err; + + /* At the center of the process we do the datum shift (if needed) */ + + err = datum_transform(src, dst, point_count, point_offset, x, y, z ); + if (err) + return err; + + /* Now get out on the other side: Bring "normal form" to output form */ + + err = geometric_to_orthometric (dst, PJ_FWD, point_count, point_offset, x, y, z); + if (err) + return err; + err = height_unit (dst, PJ_FWD, point_count, point_offset, z); + if (err) + return err; + err = prime_meridian (dst, PJ_FWD, point_count, point_offset, x); + if (err) + return err; + err = geographic_to_cartesian (dst, PJ_FWD, point_count, point_offset, x, y, z); + if (err) + return err; + err = geographic_to_projected (dst, point_count, point_offset, x, y, z); + if (err) + return err; + err = long_wrap (dst, point_count, point_offset, x); + if (err) + return err; + err = adjust_axes (dst, PJ_FWD, point_count, point_offset, x, y, z); + if (err) + return err; + + return 0; +} + + + +/************************************************************************/ +/* pj_geodetic_to_geocentric() */ +/************************************************************************/ + +int pj_geodetic_to_geocentric( double a, double es, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double b; + int i; + GeocentricInfo gi; + int ret_errno = 0; + + if( es == 0.0 ) + b = a; + else + b = a * sqrt(1-es); + + if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) + { + return PJD_ERR_GEOCENTRIC; + } + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io], + x+io, y+io, z+io ) != 0 ) + { + ret_errno = PJD_ERR_LAT_OR_LON_EXCEED_LIMIT; + x[io] = y[io] = HUGE_VAL; + /* but keep processing points! */ + } + } + + return ret_errno; +} + +/************************************************************************/ +/* pj_geocentric_to_geodetic() */ +/************************************************************************/ + +int pj_geocentric_to_geodetic( double a, double es, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double b; + int i; + GeocentricInfo gi; + + if( es == 0.0 ) + b = a; + else + b = a * sqrt(1-es); + + if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) + { + return PJD_ERR_GEOCENTRIC; + } + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io], + y+io, x+io, z+io ); + } + + return 0; +} + +/************************************************************************/ +/* pj_compare_datums() */ +/* */ +/* Returns TRUE if the two datums are identical, otherwise */ +/* FALSE. */ +/************************************************************************/ + +int pj_compare_datums( PJ *srcdefn, PJ *dstdefn ) + +{ + if( srcdefn->datum_type != dstdefn->datum_type ) + { + return 0; + } + else if( srcdefn->a_orig != dstdefn->a_orig + || ABS(srcdefn->es_orig - dstdefn->es_orig) > 0.000000000050 ) + { + /* the tolerance for es is to ensure that GRS80 and WGS84 are + considered identical */ + return 0; + } + else if( srcdefn->datum_type == PJD_3PARAM ) + { + return (srcdefn->datum_params[0] == dstdefn->datum_params[0] + && srcdefn->datum_params[1] == dstdefn->datum_params[1] + && srcdefn->datum_params[2] == dstdefn->datum_params[2]); + } + else if( srcdefn->datum_type == PJD_7PARAM ) + { + return (srcdefn->datum_params[0] == dstdefn->datum_params[0] + && srcdefn->datum_params[1] == dstdefn->datum_params[1] + && srcdefn->datum_params[2] == dstdefn->datum_params[2] + && srcdefn->datum_params[3] == dstdefn->datum_params[3] + && srcdefn->datum_params[4] == dstdefn->datum_params[4] + && srcdefn->datum_params[5] == dstdefn->datum_params[5] + && srcdefn->datum_params[6] == dstdefn->datum_params[6]); + } + else if( srcdefn->datum_type == PJD_GRIDSHIFT ) + { + const char* srcnadgrids = + pj_param(srcdefn->ctx, srcdefn->params,"snadgrids").s; + const char* dstnadgrids = + pj_param(dstdefn->ctx, dstdefn->params,"snadgrids").s; + return srcnadgrids != nullptr && dstnadgrids != nullptr && + strcmp( srcnadgrids, dstnadgrids ) == 0; + } + else + return 1; +} + +/************************************************************************/ +/* pj_geocentic_to_wgs84() */ +/************************************************************************/ + +static +int pj_geocentric_to_wgs84( PJ *defn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + + if( defn->datum_type == PJD_3PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + x[io] = x[io] + Dx_BF; + y[io] = y[io] + Dy_BF; + z[io] = z[io] + Dz_BF; + } + } + else if( defn->datum_type == PJD_7PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + double x_out, y_out, z_out; + + if( x[io] == HUGE_VAL ) + continue; + + x_out = M_BF*( x[io] - Rz_BF*y[io] + Ry_BF*z[io]) + Dx_BF; + y_out = M_BF*( Rz_BF*x[io] + y[io] - Rx_BF*z[io]) + Dy_BF; + z_out = M_BF*(-Ry_BF*x[io] + Rx_BF*y[io] + z[io]) + Dz_BF; + + x[io] = x_out; + y[io] = y_out; + z[io] = z_out; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_geocentic_from_wgs84() */ +/************************************************************************/ + +static +int pj_geocentric_from_wgs84( PJ *defn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + + if( defn->datum_type == PJD_3PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + x[io] = x[io] - Dx_BF; + y[io] = y[io] - Dy_BF; + z[io] = z[io] - Dz_BF; + } + } + else if( defn->datum_type == PJD_7PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + double x_tmp, y_tmp, z_tmp; + + if( x[io] == HUGE_VAL ) + continue; + + x_tmp = (x[io] - Dx_BF) / M_BF; + y_tmp = (y[io] - Dy_BF) / M_BF; + z_tmp = (z[io] - Dz_BF) / M_BF; + + x[io] = x_tmp + Rz_BF*y_tmp - Ry_BF*z_tmp; + y[io] = -Rz_BF*x_tmp + y_tmp + Rx_BF*z_tmp; + z[io] = Ry_BF*x_tmp - Rx_BF*y_tmp + z_tmp; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_datum_transform() */ +/* */ +/* The input should be long/lat/z coordinates in radians in the */ +/* source datum, and the output should be long/lat/z */ +/* coordinates in radians in the destination datum. */ +/************************************************************************/ + +int pj_datum_transform( PJ *src, PJ *dst, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double src_a, src_es, dst_a, dst_es; + int z_is_temp = FALSE; + +/* -------------------------------------------------------------------- */ +/* We cannot do any meaningful datum transformation if either */ +/* the source or destination are of an unknown datum type */ +/* (ie. only a +ellps declaration, no +datum). This is new */ +/* behavior for PROJ 4.6.0. */ +/* -------------------------------------------------------------------- */ + if( src->datum_type == PJD_UNKNOWN + || dst->datum_type == PJD_UNKNOWN ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Short cut if the datums are identical. */ +/* -------------------------------------------------------------------- */ + if( pj_compare_datums( src, dst ) ) + return 0; + + src_a = src->a_orig; + src_es = src->es_orig; + + dst_a = dst->a_orig; + dst_es = dst->es_orig; + +/* -------------------------------------------------------------------- */ +/* Create a temporary Z array if one is not provided. */ +/* -------------------------------------------------------------------- */ + if( z == nullptr ) + { + size_t bytes = sizeof(double) * point_count * point_offset; + z = (double *) pj_malloc(bytes); + memset( z, 0, bytes ); + z_is_temp = TRUE; + } + +#define CHECK_RETURN(defn) {if( defn->ctx->last_errno != 0 && (defn->ctx->last_errno > 0 || get_transient_error_value(-defn->ctx->last_errno) == 0) ) { if( z_is_temp ) pj_dalloc(z); return defn->ctx->last_errno; }} + +/* -------------------------------------------------------------------- */ +/* If this datum requires grid shifts, then apply it to geodetic */ +/* coordinates. */ +/* -------------------------------------------------------------------- */ + if( src->datum_type == PJD_GRIDSHIFT ) + { + pj_apply_gridshift_2( src, 0, point_count, point_offset, x, y, z ); + CHECK_RETURN(src); + + src_a = SRS_WGS84_SEMIMAJOR; + src_es = SRS_WGS84_ESQUARED; + } + + if( dst->datum_type == PJD_GRIDSHIFT ) + { + dst_a = SRS_WGS84_SEMIMAJOR; + dst_es = SRS_WGS84_ESQUARED; + } + +/* ==================================================================== */ +/* Do we need to go through geocentric coordinates? */ +/* ==================================================================== */ + if( src_es != dst_es || src_a != dst_a + || src->datum_type == PJD_3PARAM + || src->datum_type == PJD_7PARAM + || dst->datum_type == PJD_3PARAM + || dst->datum_type == PJD_7PARAM) + { +/* -------------------------------------------------------------------- */ +/* Convert to geocentric coordinates. */ +/* -------------------------------------------------------------------- */ + src->ctx->last_errno = + pj_geodetic_to_geocentric( src_a, src_es, + point_count, point_offset, x, y, z ); + CHECK_RETURN(src); + +/* -------------------------------------------------------------------- */ +/* Convert between datums. */ +/* -------------------------------------------------------------------- */ + if( src->datum_type == PJD_3PARAM + || src->datum_type == PJD_7PARAM ) + { + pj_geocentric_to_wgs84( src, point_count, point_offset,x,y,z); + CHECK_RETURN(src); + } + + if( dst->datum_type == PJD_3PARAM + || dst->datum_type == PJD_7PARAM ) + { + pj_geocentric_from_wgs84( dst, point_count,point_offset,x,y,z); + CHECK_RETURN(dst); + } + +/* -------------------------------------------------------------------- */ +/* Convert back to geodetic coordinates. */ +/* -------------------------------------------------------------------- */ + dst->ctx->last_errno = + pj_geocentric_to_geodetic( dst_a, dst_es, + point_count, point_offset, x, y, z ); + CHECK_RETURN(dst); + } + +/* -------------------------------------------------------------------- */ +/* Apply grid shift to destination if required. */ +/* -------------------------------------------------------------------- */ + if( dst->datum_type == PJD_GRIDSHIFT ) + { + pj_apply_gridshift_2( dst, 1, point_count, point_offset, x, y, z ); + CHECK_RETURN(dst); + } + + if( z_is_temp ) + pj_dalloc( z ); + + return 0; +} + +/************************************************************************/ +/* adjust_axis() */ +/* */ +/* Normalize or de-normalized the x/y/z axes. The normal form */ +/* is "enu" (easting, northing, up). */ +/************************************************************************/ +static int adjust_axis( projCtx ctx, + const char *axis, int denormalize_flag, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double x_in, y_in, z_in = 0.0; + int i, i_axis; + + if( !denormalize_flag ) + { + for( i = 0; i < point_count; i++ ) + { + x_in = x[point_offset*i]; + y_in = y[point_offset*i]; + if( z ) + z_in = z[point_offset*i]; + + for( i_axis = 0; i_axis < 3; i_axis++ ) + { + double value; + + if( i_axis == 0 ) + value = x_in; + else if( i_axis == 1 ) + value = y_in; + else + value = z_in; + + switch( axis[i_axis] ) + { + case 'e': + x[point_offset*i] = value; + break; + case 'w': + x[point_offset*i] = -value; + break; + case 'n': + y[point_offset*i] = value; + break; + case 's': + y[point_offset*i] = -value; + break; + case 'u': + if( z ) + z[point_offset*i] = value; + break; + case 'd': + if( z ) + z[point_offset*i] = -value; + break; + default: + pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); + return PJD_ERR_AXIS; + } + } /* i_axis */ + } /* i (point) */ + } + + else /* denormalize */ + { + for( i = 0; i < point_count; i++ ) + { + x_in = x[point_offset*i]; + y_in = y[point_offset*i]; + if( z ) + z_in = z[point_offset*i]; + + for( i_axis = 0; i_axis < 3; i_axis++ ) + { + double *target; + + if( i_axis == 2 && z == nullptr ) + continue; + + if( i_axis == 0 ) + target = x; + else if( i_axis == 1 ) + target = y; + else + target = z; + + switch( axis[i_axis] ) + { + case 'e': + target[point_offset*i] = x_in; break; + case 'w': + target[point_offset*i] = -x_in; break; + case 'n': + target[point_offset*i] = y_in; break; + case 's': + target[point_offset*i] = -y_in; break; + case 'u': + target[point_offset*i] = z_in; break; + case 'd': + target[point_offset*i] = -z_in; break; + default: + pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); + return PJD_ERR_AXIS; + } + } /* i_axis */ + } /* i (point) */ + } + + return 0; +} |
