// Boost.Geometry // This file is manually converted from PROJ4 // This file was modified by Oracle on 2017, 2018. // Modifications copyright (c) 2017-2018, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // This file is converted from PROJ4, http://trac.osgeo.org/proj // PROJ4 is originally written by Gerald Evenden (then of the USGS) // PROJ4 is maintained by Frank Warmerdam // This file was converted to Geometry Library by Adam Wulkiewicz // Original copyright notice: // 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. #ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_TRANSFORM_HPP #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_TRANSFORM_HPP #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { namespace projections { namespace detail { // ----------------------------------------------------------- // Boost.Geometry helpers begin // ----------------------------------------------------------- template < typename Point, bool HasCoord2 = (dimension::value > 2) > struct z_access { typedef typename coordinate_type::type type; static inline type get(Point const& point) { return geometry::get<2>(point); } static inline void set(Point & point, type const& h) { return geometry::set<2>(point, h); } }; template struct z_access { typedef typename coordinate_type::type type; static inline type get(Point const& ) { return type(0); } static inline void set(Point & , type const& ) {} }; template inline typename z_access::type get_z(XYorXYZ const& xy_or_xyz) { return z_access::get(xy_or_xyz); } template inline void set_z(XYorXYZ & xy_or_xyz, typename z_access::type const& z) { return z_access::set(xy_or_xyz, z); } template < typename Range, bool AddZ = (dimension::type>::value < 3) > struct range_wrapper { typedef Range range_type; typedef typename boost::range_value::type point_type; typedef typename coordinate_type::type coord_t; range_wrapper(Range & range) : m_range(range) {} range_type & get_range() { return m_range; } coord_t get_z(std::size_t i) { return detail::get_z(range::at(m_range, i)); } void set_z(std::size_t i, coord_t const& v) { return detail::set_z(range::at(m_range, i), v); } private: Range & m_range; }; template struct range_wrapper { typedef Range range_type; typedef typename boost::range_value::type point_type; typedef typename coordinate_type::type coord_t; range_wrapper(Range & range) : m_range(range) , m_zs(boost::size(range), coord_t(0)) {} range_type & get_range() { return m_range; } coord_t get_z(std::size_t i) { return m_zs[i]; } void set_z(std::size_t i, coord_t const& v) { m_zs[i] = v; } private: Range & m_range; std::vector m_zs; }; // ----------------------------------------------------------- // Boost.Geometry helpers end // ----------------------------------------------------------- template inline typename Par::type Dx_BF(Par const& defn) { return defn.datum_params[0]; } template inline typename Par::type Dy_BF(Par const& defn) { return defn.datum_params[1]; } template inline typename Par::type Dz_BF(Par const& defn) { return defn.datum_params[2]; } template inline typename Par::type Rx_BF(Par const& defn) { return defn.datum_params[3]; } template inline typename Par::type Ry_BF(Par const& defn) { return defn.datum_params[4]; } template inline typename Par::type Rz_BF(Par const& defn) { return defn.datum_params[5]; } template inline typename Par::type M_BF(Par const& defn) { return defn.datum_params[6]; } /* ** This table is intended to indicate for any given error code in ** the range 0 to -56, 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 }; template inline int pj_geocentric_to_geodetic( T const& a, T const& es, Range & range ); template inline int pj_geodetic_to_geocentric( T const& a, T const& es, Range & range ); /************************************************************************/ /* 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). */ /************************************************************************/ template < typename SrcPrj, typename DstPrj2, typename Par, typename Range, typename Grids > inline bool pj_transform(SrcPrj const& srcprj, Par const& srcdefn, DstPrj2 const& dstprj, Par const& dstdefn, Range & range, Grids const& srcgrids, Grids const& dstgrids) { typedef typename boost::range_value::type point_type; typedef typename coordinate_type::type coord_t; static const std::size_t dimension = geometry::dimension::value; std::size_t point_count = boost::size(range); bool result = true; /* -------------------------------------------------------------------- */ /* Transform unusual input coordinate axis orientation to */ /* standard form if needed. */ /* -------------------------------------------------------------------- */ // Ignored /* -------------------------------------------------------------------- */ /* Transform Z to meters if it isn't already. */ /* -------------------------------------------------------------------- */ if( srcdefn.vto_meter != 1.0 && dimension > 2 ) { for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = geometry::range::at(range, i); set_z(point, get_z(point) * srcdefn.vto_meter); } } /* -------------------------------------------------------------------- */ /* Transform geocentric source coordinates to lat/long. */ /* -------------------------------------------------------------------- */ if( srcdefn.is_geocent ) { // Point should be cartesian 3D (ECEF) if (dimension < 3) BOOST_THROW_EXCEPTION( projection_exception(error_geocentric) ); //return PJD_ERR_GEOCENTRIC; if( srcdefn.to_meter != 1.0 ) { for(std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); if( ! is_invalid_point(point) ) { set<0>(point, get<0>(point) * srcdefn.to_meter); set<1>(point, get<1>(point) * srcdefn.to_meter); } } } range_wrapper rng(range); int err = pj_geocentric_to_geodetic( srcdefn.a_orig, srcdefn.es_orig, rng ); if( err != 0 ) BOOST_THROW_EXCEPTION( projection_exception(err) ); //return err; // NOTE: here 3D cartesian ECEF is converted into 3D geodetic LLH } /* -------------------------------------------------------------------- */ /* Transform source points to lat/long, if they aren't */ /* already. */ /* -------------------------------------------------------------------- */ else if( !srcdefn.is_latlong ) { // Point should be cartesian 2D or 3D (map projection) /* Check first if projection is invertible. */ /*if( (srcdefn->inv3d == NULL) && (srcdefn->inv == NULL)) { pj_ctx_set_errno( pj_get_ctx(srcdefn), -17 ); pj_log( pj_get_ctx(srcdefn), PJ_LOG_ERROR, "pj_transform(): source projection not invertible" ); return -17; }*/ /* If invertible - First try inv3d if defined */ //if (srcdefn->inv3d != NULL) //{ // /* Three dimensions must be defined */ // if ( z == NULL) // { // pj_ctx_set_errno( pj_get_ctx(srcdefn), PJD_ERR_GEOCENTRIC); // return PJD_ERR_GEOCENTRIC; // } // for (i=0; i < point_count; i++) // { // XYZ projected_loc; // XYZ geodetic_loc; // projected_loc.u = x[point_offset*i]; // projected_loc.v = y[point_offset*i]; // projected_loc.w = z[point_offset*i]; // if (projected_loc.u == HUGE_VAL) // continue; // geodetic_loc = pj_inv3d(projected_loc, srcdefn); // if( srcdefn->ctx->last_errno != 0 ) // { // if( (srcdefn->ctx->last_errno != 33 /*EDOM*/ // && srcdefn->ctx->last_errno != 34 /*ERANGE*/ ) // && (srcdefn->ctx->last_errno > 0 // || srcdefn->ctx->last_errno < -44 || point_count == 1 // || transient_error[-srcdefn->ctx->last_errno] == 0 ) ) // return srcdefn->ctx->last_errno; // else // { // geodetic_loc.u = HUGE_VAL; // geodetic_loc.v = HUGE_VAL; // geodetic_loc.w = HUGE_VAL; // } // } // x[point_offset*i] = geodetic_loc.u; // y[point_offset*i] = geodetic_loc.v; // z[point_offset*i] = geodetic_loc.w; // } //} //else { /* Fallback to the original PROJ.4 API 2d inversion - inv */ for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); if( is_invalid_point(point) ) continue; try { pj_inv(srcprj, srcdefn, point, point); } catch(projection_exception const& e) { if( (e.code() != 33 /*EDOM*/ && e.code() != 34 /*ERANGE*/ ) && (e.code() > 0 || e.code() < -44 /*|| point_count == 1*/ || transient_error[-e.code()] == 0) ) { BOOST_RETHROW } else { set_invalid_point(point); result = false; if (point_count == 1) return result; } } } } } /* -------------------------------------------------------------------- */ /* But if they are already lat long, adjust for the prime */ /* meridian if there is one in effect. */ /* -------------------------------------------------------------------- */ if( srcdefn.from_greenwich != 0.0 ) { for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); if( ! is_invalid_point(point) ) set<0>(point, get<0>(point) + srcdefn.from_greenwich); } } /* -------------------------------------------------------------------- */ /* Do we need to translate from geoid to ellipsoidal vertical */ /* datum? */ /* -------------------------------------------------------------------- */ /*if( srcdefn->has_geoid_vgrids && z != NULL ) { if( pj_apply_vgridshift( srcdefn, "sgeoidgrids", &(srcdefn->vgridlist_geoid), &(srcdefn->vgridlist_geoid_count), 0, point_count, point_offset, x, y, z ) != 0 ) return pj_ctx_get_errno(srcdefn->ctx); }*/ /* -------------------------------------------------------------------- */ /* Convert datums if needed, and possible. */ /* -------------------------------------------------------------------- */ if ( ! pj_datum_transform( srcdefn, dstdefn, range, srcgrids, dstgrids ) ) { result = false; } /* -------------------------------------------------------------------- */ /* Do we need to translate from ellipsoidal to geoid vertical */ /* datum? */ /* -------------------------------------------------------------------- */ /*if( dstdefn->has_geoid_vgrids && z != NULL ) { if( pj_apply_vgridshift( dstdefn, "sgeoidgrids", &(dstdefn->vgridlist_geoid), &(dstdefn->vgridlist_geoid_count), 1, point_count, point_offset, x, y, z ) != 0 ) return dstdefn->ctx->last_errno; }*/ /* -------------------------------------------------------------------- */ /* But if they are staying lat long, adjust for the prime */ /* meridian if there is one in effect. */ /* -------------------------------------------------------------------- */ if( dstdefn.from_greenwich != 0.0 ) { for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); if( ! is_invalid_point(point) ) set<0>(point, get<0>(point) - dstdefn.from_greenwich); } } /* -------------------------------------------------------------------- */ /* Transform destination latlong to geocentric if required. */ /* -------------------------------------------------------------------- */ if( dstdefn.is_geocent ) { // Point should be cartesian 3D (ECEF) if (dimension < 3) BOOST_THROW_EXCEPTION( projection_exception(error_geocentric) ); //return PJD_ERR_GEOCENTRIC; // NOTE: In the original code the return value of the following // function is not checked range_wrapper rng(range); int err = pj_geodetic_to_geocentric( dstdefn.a_orig, dstdefn.es_orig, rng ); if( err == -14 ) result = false; else BOOST_THROW_EXCEPTION( projection_exception(err) ); if( dstdefn.fr_meter != 1.0 ) { for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); if( ! is_invalid_point(point) ) { set<0>(point, get<0>(point) * dstdefn.fr_meter); set<1>(point, get<1>(point) * dstdefn.fr_meter); } } } } /* -------------------------------------------------------------------- */ /* Transform destination points to projection coordinates, if */ /* desired. */ /* -------------------------------------------------------------------- */ else if( !dstdefn.is_latlong ) { //if( dstdefn->fwd3d != NULL) //{ // for( i = 0; i < point_count; i++ ) // { // XYZ projected_loc; // LPZ geodetic_loc; // geodetic_loc.u = x[point_offset*i]; // geodetic_loc.v = y[point_offset*i]; // geodetic_loc.w = z[point_offset*i]; // if (geodetic_loc.u == HUGE_VAL) // continue; // projected_loc = pj_fwd3d( geodetic_loc, dstdefn); // if( dstdefn->ctx->last_errno != 0 ) // { // if( (dstdefn->ctx->last_errno != 33 /*EDOM*/ // && dstdefn->ctx->last_errno != 34 /*ERANGE*/ ) // && (dstdefn->ctx->last_errno > 0 // || dstdefn->ctx->last_errno < -44 || point_count == 1 // || transient_error[-dstdefn->ctx->last_errno] == 0 ) ) // return dstdefn->ctx->last_errno; // else // { // projected_loc.u = HUGE_VAL; // projected_loc.v = HUGE_VAL; // projected_loc.w = HUGE_VAL; // } // } // x[point_offset*i] = projected_loc.u; // y[point_offset*i] = projected_loc.v; // z[point_offset*i] = projected_loc.w; // } //} //else { for(std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); if( is_invalid_point(point) ) continue; try { pj_fwd(dstprj, dstdefn, point, point); } catch (projection_exception const& e) { if( (e.code() != 33 /*EDOM*/ && e.code() != 34 /*ERANGE*/ ) && (e.code() > 0 || e.code() < -44 /*|| point_count == 1*/ || transient_error[-e.code()] == 0) ) { BOOST_RETHROW } else { set_invalid_point(point); result = false; if (point_count == 1) return result; } } } } } /* -------------------------------------------------------------------- */ /* If a wrapping center other than 0 is provided, rewrap around */ /* the suggested center (for latlong coordinate systems only). */ /* -------------------------------------------------------------------- */ else if( dstdefn.is_latlong && dstdefn.is_long_wrap_set ) { for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(range, i); coord_t x = get_as_radian<0>(point); if( is_invalid_point(point) ) continue; // TODO - units-dependant constants could be used instead while( x < dstdefn.long_wrap_center - math::pi() ) x += math::two_pi(); while( x > dstdefn.long_wrap_center + math::pi() ) x -= math::two_pi(); set_from_radian<0>(point, x); } } /* -------------------------------------------------------------------- */ /* Transform Z from meters if needed. */ /* -------------------------------------------------------------------- */ if( dstdefn.vto_meter != 1.0 && dimension > 2 ) { for( std::size_t i = 0; i < point_count; i++ ) { point_type & point = geometry::range::at(range, i); set_z(point, get_z(point) * dstdefn.vfr_meter); } } /* -------------------------------------------------------------------- */ /* Transform normalized axes into unusual output coordinate axis */ /* orientation if needed. */ /* -------------------------------------------------------------------- */ // Ignored return result; } /************************************************************************/ /* pj_geodetic_to_geocentric() */ /************************************************************************/ template inline int pj_geodetic_to_geocentric( T const& a, T const& es, range_wrapper & range_wrapper ) { //typedef typename boost::range_iterator::type iterator; typedef typename boost::range_value::type point_type; //typedef typename coordinate_type::type coord_t; Range & rng = range_wrapper.get_range(); std::size_t point_count = boost::size(rng); int ret_errno = 0; T const b = (es == 0.0) ? a : a * sqrt(1-es); GeocentricInfo gi; if( pj_Set_Geocentric_Parameters( gi, a, b ) != 0 ) { return error_geocentric; } for( std::size_t i = 0 ; i < point_count ; ++i ) { point_type & point = range::at(rng, i); if( is_invalid_point(point) ) continue; T X = 0, Y = 0, Z = 0; if( pj_Convert_Geodetic_To_Geocentric( gi, get_as_radian<0>(point), get_as_radian<1>(point), range_wrapper.get_z(i), // Height X, Y, Z ) != 0 ) { ret_errno = error_lat_or_lon_exceed_limit; set_invalid_point(point); /* but keep processing points! */ } else { set<0>(point, X); set<1>(point, Y); range_wrapper.set_z(i, Z); } } return ret_errno; } /************************************************************************/ /* pj_geodetic_to_geocentric() */ /************************************************************************/ template inline int pj_geocentric_to_geodetic( T const& a, T const& es, range_wrapper & range_wrapper ) { //typedef typename boost::range_iterator::type iterator; typedef typename boost::range_value::type point_type; //typedef typename coordinate_type::type coord_t; Range & rng = range_wrapper.get_range(); std::size_t point_count = boost::size(rng); T const b = (es == 0.0) ? a : a * sqrt(1-es); GeocentricInfo gi; if( pj_Set_Geocentric_Parameters( gi, a, b ) != 0 ) { return error_geocentric; } for( std::size_t i = 0 ; i < point_count ; ++i ) { point_type & point = range::at(rng, i); if( is_invalid_point(point) ) continue; T Longitude = 0, Latitude = 0, Height = 0; pj_Convert_Geocentric_To_Geodetic( gi, get<0>(point), get<1>(point), range_wrapper.get_z(i), // z Longitude, Latitude, Height ); set_from_radian<0>(point, Longitude); set_from_radian<1>(point, Latitude); range_wrapper.set_z(i, Height); // Height } return 0; } /************************************************************************/ /* pj_compare_datums() */ /* */ /* Returns TRUE if the two datums are identical, otherwise */ /* FALSE. */ /************************************************************************/ template inline bool pj_compare_datums( Par & srcdefn, Par & dstdefn ) { if( srcdefn.datum_type != dstdefn.datum_type ) { return false; } else if( srcdefn.a_orig != dstdefn.a_orig || math::abs(srcdefn.es_orig - dstdefn.es_orig) > 0.000000000050 ) { /* the tolerance for es is to ensure that GRS80 and WGS84 are considered identical */ return false; } else if( srcdefn.datum_type == datum_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 == datum_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 == datum_gridshift ) { return srcdefn.nadgrids == dstdefn.nadgrids; } else return true; } /************************************************************************/ /* pj_geocentic_to_wgs84() */ /************************************************************************/ template inline int pj_geocentric_to_wgs84( Par const& defn, range_wrapper & range_wrapper ) { typedef typename boost::range_value::type point_type; typedef typename coordinate_type::type coord_t; Range & rng = range_wrapper.get_range(); std::size_t point_count = boost::size(rng); if( defn.datum_type == datum_3param ) { for(std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(rng, i); if( is_invalid_point(point) ) continue; set<0>(point, get<0>(point) + Dx_BF(defn)); set<1>(point, get<1>(point) + Dy_BF(defn)); range_wrapper.set_z(i, range_wrapper.get_z(i) + Dz_BF(defn)); } } else if( defn.datum_type == datum_7param ) { for(std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(rng, i); if( is_invalid_point(point) ) continue; coord_t x = get<0>(point); coord_t y = get<1>(point); coord_t z = range_wrapper.get_z(i); coord_t x_out, y_out, z_out; x_out = M_BF(defn)*( x - Rz_BF(defn)*y + Ry_BF(defn)*z) + Dx_BF(defn); y_out = M_BF(defn)*( Rz_BF(defn)*x + y - Rx_BF(defn)*z) + Dy_BF(defn); z_out = M_BF(defn)*(-Ry_BF(defn)*x + Rx_BF(defn)*y + z) + Dz_BF(defn); set<0>(point, x_out); set<1>(point, y_out); range_wrapper.set_z(i, z_out); } } return 0; } /************************************************************************/ /* pj_geocentic_from_wgs84() */ /************************************************************************/ template inline int pj_geocentric_from_wgs84( Par const& defn, range_wrapper & range_wrapper ) { typedef typename boost::range_value::type point_type; typedef typename coordinate_type::type coord_t; Range & rng = range_wrapper.get_range(); std::size_t point_count = boost::size(rng); if( defn.datum_type == datum_3param ) { for(std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(rng, i); if( is_invalid_point(point) ) continue; set<0>(point, get<0>(point) - Dx_BF(defn)); set<1>(point, get<1>(point) - Dy_BF(defn)); range_wrapper.set_z(i, range_wrapper.get_z(i) - Dz_BF(defn)); } } else if( defn.datum_type == datum_7param ) { for(std::size_t i = 0; i < point_count; i++ ) { point_type & point = range::at(rng, i); if( is_invalid_point(point) ) continue; coord_t x = get<0>(point); coord_t y = get<1>(point); coord_t z = range_wrapper.get_z(i); coord_t x_tmp = (x - Dx_BF(defn)) / M_BF(defn); coord_t y_tmp = (y - Dy_BF(defn)) / M_BF(defn); coord_t z_tmp = (z - Dz_BF(defn)) / M_BF(defn); x = x_tmp + Rz_BF(defn)*y_tmp - Ry_BF(defn)*z_tmp; y = -Rz_BF(defn)*x_tmp + y_tmp + Rx_BF(defn)*z_tmp; z = Ry_BF(defn)*x_tmp - Rx_BF(defn)*y_tmp + z_tmp; set<0>(point, x); set<1>(point, y); range_wrapper.set_z(i, z); } } return 0; } inline bool pj_datum_check_error(int err) { return err != 0 && (err > 0 || transient_error[-err] == 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. */ /************************************************************************/ template inline bool pj_datum_transform(Par const& srcdefn, Par const& dstdefn, Range & range, Grids const& srcgrids, Grids const& dstgrids) { typedef typename Par::type calc_t; // This has to be consistent with default spheroid and pj_ellps // TODO: Define in one place static const calc_t wgs84_a = 6378137.0; static const calc_t wgs84_b = 6356752.3142451793; static const calc_t wgs84_es = 1. - (wgs84_b * wgs84_b) / (wgs84_a * wgs84_a); bool result = true; calc_t src_a, src_es, dst_a, dst_es; /* -------------------------------------------------------------------- */ /* 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( srcdefn.datum_type == datum_unknown || dstdefn.datum_type == datum_unknown ) return result; /* -------------------------------------------------------------------- */ /* Short cut if the datums are identical. */ /* -------------------------------------------------------------------- */ if( pj_compare_datums( srcdefn, dstdefn ) ) return result; src_a = srcdefn.a_orig; src_es = srcdefn.es_orig; dst_a = dstdefn.a_orig; dst_es = dstdefn.es_orig; /* -------------------------------------------------------------------- */ /* Create a temporary Z array if one is not provided. */ /* -------------------------------------------------------------------- */ range_wrapper z_range(range); /* -------------------------------------------------------------------- */ /* If this datum requires grid shifts, then apply it to geodetic */ /* coordinates. */ /* -------------------------------------------------------------------- */ if( srcdefn.datum_type == datum_gridshift ) { try { pj_apply_gridshift_2( srcdefn, range, srcgrids ); } catch (projection_exception const& e) { if (pj_datum_check_error(e.code())) { BOOST_RETHROW } } src_a = wgs84_a; src_es = wgs84_es; } if( dstdefn.datum_type == datum_gridshift ) { dst_a = wgs84_a; dst_es = wgs84_es; } /* ==================================================================== */ /* Do we need to go through geocentric coordinates? */ /* ==================================================================== */ if( src_es != dst_es || src_a != dst_a || srcdefn.datum_type == datum_3param || srcdefn.datum_type == datum_7param || dstdefn.datum_type == datum_3param || dstdefn.datum_type == datum_7param) { /* -------------------------------------------------------------------- */ /* Convert to geocentric coordinates. */ /* -------------------------------------------------------------------- */ int err = pj_geodetic_to_geocentric( src_a, src_es, z_range ); if (pj_datum_check_error(err)) BOOST_THROW_EXCEPTION( projection_exception(err) ); else if (err != 0) result = false; /* -------------------------------------------------------------------- */ /* Convert between datums. */ /* -------------------------------------------------------------------- */ if( srcdefn.datum_type == datum_3param || srcdefn.datum_type == datum_7param ) { try { pj_geocentric_to_wgs84( srcdefn, z_range ); } catch (projection_exception const& e) { if (pj_datum_check_error(e.code())) { BOOST_RETHROW } } } if( dstdefn.datum_type == datum_3param || dstdefn.datum_type == datum_7param ) { try { pj_geocentric_from_wgs84( dstdefn, z_range ); } catch (projection_exception const& e) { if (pj_datum_check_error(e.code())) { BOOST_RETHROW } } } /* -------------------------------------------------------------------- */ /* Convert back to geodetic coordinates. */ /* -------------------------------------------------------------------- */ err = pj_geocentric_to_geodetic( dst_a, dst_es, z_range ); if (pj_datum_check_error(err)) BOOST_THROW_EXCEPTION( projection_exception(err) ); else if (err != 0) result = false; } /* -------------------------------------------------------------------- */ /* Apply grid shift to destination if required. */ /* -------------------------------------------------------------------- */ if( dstdefn.datum_type == datum_gridshift ) { try { pj_apply_gridshift_2( dstdefn, range, dstgrids ); } catch (projection_exception const& e) { if (pj_datum_check_error(e.code())) BOOST_RETHROW } } return result; } } // namespace detail }}} // namespace boost::geometry::projections #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_TRANSFORM_HPP