diff options
Diffstat (limited to 'src/pj_transform.c')
| -rw-r--r-- | src/pj_transform.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/pj_transform.c b/src/pj_transform.c new file mode 100644 index 00000000..9bdd4314 --- /dev/null +++ b/src/pj_transform.c @@ -0,0 +1,459 @@ +/****************************************************************************** + * $Id$ + * + * Project: PROJ.4 + * Purpose: Perform overall coordinate system to coordinate system + * transformations (pj_transform() function) including reprojection + * and datum shifting. + * Author: Frank Warmerdam, warmerda@home.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. + ****************************************************************************** + * + * $Log$ + * Revision 1.1 2000/07/06 23:32:27 warmerda + * New + * + */ + +#include <projects.h> +#include <string.h> +#include <math.h> +#include "geocent.h" + +#ifndef SRS_WGS84_SEMIMAJOR +#define SRS_WGS84_SEMIMAJOR 6378137.0 +#endif + +#ifndef SRS_WGS84_ESQUARED +#define SRS_WGS84_ESQUARED 0.006694379990 +#endif + +/************************************************************************/ +/* 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 *srcdefn, PJ *dstdefn, long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + long i; + int need_datum_shift; + + pj_errno = 0; + + if( point_offset == 0 ) + point_offset = 1; + +/* -------------------------------------------------------------------- */ +/* Transform source points to lat/long, if they aren't */ +/* already. */ +/* -------------------------------------------------------------------- */ + if( !srcdefn->is_latlong ) + { + for( i = 0; i < point_count; i++ ) + { + XY projected_loc; + LP geodetic_loc; + + projected_loc.u = x[point_offset*i]; + projected_loc.v = y[point_offset*i]; + + geodetic_loc = pj_inv( projected_loc, srcdefn ); + if( pj_errno != 0 ) + return pj_errno; + + x[point_offset*i] = geodetic_loc.u; + y[point_offset*i] = geodetic_loc.v; + } + } + +/* -------------------------------------------------------------------- */ +/* Convert datums if needed, and possible. */ +/* -------------------------------------------------------------------- */ + if( pj_datum_transform( srcdefn, dstdefn, point_count, point_offset, + x, y, z ) != 0 ) + return pj_errno; + +/* -------------------------------------------------------------------- */ +/* Transform destination points to projection coordinates, if */ +/* desired. */ +/* -------------------------------------------------------------------- */ + if( !dstdefn->is_latlong ) + { + for( i = 0; i < point_count; i++ ) + { + XY projected_loc; + LP geodetic_loc; + + geodetic_loc.u = x[point_offset*i]; + geodetic_loc.v = y[point_offset*i]; + + projected_loc = pj_fwd( geodetic_loc, dstdefn ); + if( pj_errno != 0 ) + return pj_errno; + + x[point_offset*i] = projected_loc.u; + y[point_offset*i] = projected_loc.v; + } + } + + 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; + + if( es == 0.0 ) + b = a; + else + b = a * sqrt(1-es); + + if( Set_Geocentric_Parameters( a, b ) != 0 ) + { + pj_errno = PJD_ERR_GEOCENTRIC; + return pj_errno; + } + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( Convert_Geodetic_To_Geocentric( y[io], x[io], z[io], + x+io, y+io, z+io ) != 0 ) + { + pj_errno = PJD_ERR_GEOCENTRIC; + return PJD_ERR_GEOCENTRIC; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_geodetic_to_geocentric() */ +/************************************************************************/ + +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; + + if( es == 0.0 ) + b = a; + else + b = a * sqrt(1-es); + + if( Set_Geocentric_Parameters( a, b ) != 0 ) + { + pj_errno = PJD_ERR_GEOCENTRIC; + return pj_errno; + } + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + Convert_Geocentric_To_Geodetic( 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 != dstdefn->a + || ABS(srcdefn->es - dstdefn->es) > 0.000000000050 ) + { + /* the tolerence 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 ) + { + return strcmp( pj_param(srcdefn->params,"snadgrids").s, + pj_param(dstdefn->params,"snadgrids").s ) == 0; + } + else + return 1; +} + +/************************************************************************/ +/* pj_geocentic_to_wgs84() */ +/************************************************************************/ + +int pj_geocentric_to_wgs84( PJ *defn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + + pj_errno = 0; + + if( defn->datum_type == PJD_3PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + x[io] = x[io] + defn->datum_params[0]; + y[io] = y[io] + defn->datum_params[1]; + z[io] = z[io] + defn->datum_params[2]; + } + } + 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; + + x_out = x[io] + defn->datum_params[0] + + y[io] * defn->datum_params[5] + + z[io] * defn->datum_params[4] + + x[io] * defn->datum_params[6]; + + y_out = y[io] + defn->datum_params[1] + + x[io] * defn->datum_params[5] + + z[io] * defn->datum_params[3] + + y[io] * defn->datum_params[6]; + + z_out = z[io] + defn->datum_params[2] + + x[io] * defn->datum_params[4] + + y[io] * defn->datum_params[3] + + z[io] * defn->datum_params[6]; + + x[io] = x_out; + y[io] = y_out; + z[io] = z_out; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_geocentic_from_wgs84() */ +/************************************************************************/ + +int pj_geocentric_from_wgs84( PJ *defn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + + pj_errno = 0; + + if( defn->datum_type == PJD_3PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + x[io] = x[io] - defn->datum_params[0]; + y[io] = y[io] - defn->datum_params[1]; + z[io] = z[io] - defn->datum_params[2]; + } + } + 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; + + x_out = x[io] - defn->datum_params[0] + - y[io] * defn->datum_params[5] + - z[io] * defn->datum_params[4] + - x[io] * defn->datum_params[6]; + + y_out = y[io] - defn->datum_params[1] + - x[io] * defn->datum_params[5] + - z[io] * defn->datum_params[3] + - y[io] * defn->datum_params[6]; + + z_out = z[io] - defn->datum_params[2] + - x[io] * defn->datum_params[4] + - y[io] * defn->datum_params[3] + - z[io] * defn->datum_params[6]; + + x[io] = x_out; + y[io] = y_out; + z[io] = z_out; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_datum_transform() */ +/************************************************************************/ + +int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double src_a, src_es, dst_a, dst_es; + + pj_errno = 0; + +/* -------------------------------------------------------------------- */ +/* Short cut if the datums are identical. */ +/* -------------------------------------------------------------------- */ + if( pj_compare_datums( srcdefn, dstdefn ) ) + return 0; + + src_a = srcdefn->a; + src_es = srcdefn->es; + + dst_a = dstdefn->a; + dst_es = dstdefn->es; + +/* -------------------------------------------------------------------- */ +/* If this datum requires grid shifts, then apply it to geodetic */ +/* coordinates. */ +/* -------------------------------------------------------------------- */ + if( srcdefn->datum_type == PJD_GRIDSHIFT ) + { + pj_apply_gridshift( pj_param(srcdefn->params,"snadgrids").s, 0, + point_count, point_offset, x, y, z ); + + if( pj_errno != 0 ) + return pj_errno; + + src_a = SRS_WGS84_SEMIMAJOR; + src_es = 0.006694379990; + } + + if( dstdefn->datum_type == PJD_GRIDSHIFT ) + { + dst_a = SRS_WGS84_SEMIMAJOR; + dst_es = 0.006694379990; + } + +/* ==================================================================== */ +/* Do we need to go through geocentric coordinates? */ +/* ==================================================================== */ + if( srcdefn->datum_type == PJD_3PARAM + || srcdefn->datum_type == PJD_7PARAM + || dstdefn->datum_type == PJD_3PARAM + || dstdefn->datum_type == PJD_7PARAM) + { +/* -------------------------------------------------------------------- */ +/* Convert to geocentric coordinates. */ +/* -------------------------------------------------------------------- */ + pj_geodetic_to_geocentric( src_a, src_es, + point_count, point_offset, x, y, z ); + + if( pj_errno ) + return pj_errno; + +/* -------------------------------------------------------------------- */ +/* Convert between datums. */ +/* -------------------------------------------------------------------- */ + if( srcdefn->datum_type != PJD_UNKNOWN + && dstdefn->datum_type != PJD_UNKNOWN ) + { + pj_geocentric_to_wgs84( srcdefn, point_count, point_offset,x,y,z); + if( pj_errno != 0 ) + return pj_errno; + + pj_geocentric_from_wgs84( dstdefn, point_count,point_offset,x,y,z); + if( pj_errno != 0 ) + return pj_errno; + } + +/* -------------------------------------------------------------------- */ +/* Convert back to geodetic coordinates. */ +/* -------------------------------------------------------------------- */ + pj_geocentric_to_geodetic( dst_a, dst_es, + point_count, point_offset, x, y, z ); + + if( pj_errno ) + return pj_errno; + } + +/* -------------------------------------------------------------------- */ +/* Apply grid shift to destination if required. */ +/* -------------------------------------------------------------------- */ + if( dstdefn->datum_type == PJD_GRIDSHIFT ) + { + pj_apply_gridshift( pj_param(dstdefn->params,"snadgrids").s, 1, + point_count, point_offset, x, y, z ); + + if( pj_errno != 0 ) + return pj_errno; + } + + return 0; +} + |
