pj_transform.hpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. // Boost.Geometry
  2. // This file is manually converted from PROJ4
  3. // This file was modified by Oracle on 2017, 2018.
  4. // Modifications copyright (c) 2017-2018, Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. // This file is converted from PROJ4, http://trac.osgeo.org/proj
  10. // PROJ4 is originally written by Gerald Evenden (then of the USGS)
  11. // PROJ4 is maintained by Frank Warmerdam
  12. // This file was converted to Geometry Library by Adam Wulkiewicz
  13. // Original copyright notice:
  14. // Copyright (c) 2000, Frank Warmerdam
  15. // Permission is hereby granted, free of charge, to any person obtaining a
  16. // copy of this software and associated documentation files (the "Software"),
  17. // to deal in the Software without restriction, including without limitation
  18. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  19. // and/or sell copies of the Software, and to permit persons to whom the
  20. // Software is furnished to do so, subject to the following conditions:
  21. // The above copyright notice and this permission notice shall be included
  22. // in all copies or substantial portions of the Software.
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  26. // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  28. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  29. // DEALINGS IN THE SOFTWARE.
  30. #ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_TRANSFORM_HPP
  31. #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_TRANSFORM_HPP
  32. #include <boost/geometry/core/access.hpp>
  33. #include <boost/geometry/core/coordinate_dimension.hpp>
  34. #include <boost/geometry/core/radian_access.hpp>
  35. #include <boost/geometry/srs/projections/impl/geocent.hpp>
  36. #include <boost/geometry/srs/projections/impl/pj_apply_gridshift.hpp>
  37. #include <boost/geometry/srs/projections/impl/projects.hpp>
  38. #include <boost/geometry/srs/projections/invalid_point.hpp>
  39. #include <boost/geometry/util/range.hpp>
  40. #include <cstring>
  41. #include <cmath>
  42. namespace boost { namespace geometry { namespace projections
  43. {
  44. namespace detail
  45. {
  46. // -----------------------------------------------------------
  47. // Boost.Geometry helpers begin
  48. // -----------------------------------------------------------
  49. template
  50. <
  51. typename Point,
  52. bool HasCoord2 = (dimension<Point>::value > 2)
  53. >
  54. struct z_access
  55. {
  56. typedef typename coordinate_type<Point>::type type;
  57. static inline type get(Point const& point)
  58. {
  59. return geometry::get<2>(point);
  60. }
  61. static inline void set(Point & point, type const& h)
  62. {
  63. return geometry::set<2>(point, h);
  64. }
  65. };
  66. template <typename Point>
  67. struct z_access<Point, false>
  68. {
  69. typedef typename coordinate_type<Point>::type type;
  70. static inline type get(Point const& )
  71. {
  72. return type(0);
  73. }
  74. static inline void set(Point & , type const& )
  75. {}
  76. };
  77. template <typename XYorXYZ>
  78. inline typename z_access<XYorXYZ>::type
  79. get_z(XYorXYZ const& xy_or_xyz)
  80. {
  81. return z_access<XYorXYZ>::get(xy_or_xyz);
  82. }
  83. template <typename XYorXYZ>
  84. inline void set_z(XYorXYZ & xy_or_xyz,
  85. typename z_access<XYorXYZ>::type const& z)
  86. {
  87. return z_access<XYorXYZ>::set(xy_or_xyz, z);
  88. }
  89. template
  90. <
  91. typename Range,
  92. bool AddZ = (dimension<typename boost::range_value<Range>::type>::value < 3)
  93. >
  94. struct range_wrapper
  95. {
  96. typedef Range range_type;
  97. typedef typename boost::range_value<Range>::type point_type;
  98. typedef typename coordinate_type<point_type>::type coord_t;
  99. range_wrapper(Range & range)
  100. : m_range(range)
  101. {}
  102. range_type & get_range() { return m_range; }
  103. coord_t get_z(std::size_t i) { return detail::get_z(range::at(m_range, i)); }
  104. void set_z(std::size_t i, coord_t const& v) { return detail::set_z(range::at(m_range, i), v); }
  105. private:
  106. Range & m_range;
  107. };
  108. template <typename Range>
  109. struct range_wrapper<Range, true>
  110. {
  111. typedef Range range_type;
  112. typedef typename boost::range_value<Range>::type point_type;
  113. typedef typename coordinate_type<point_type>::type coord_t;
  114. range_wrapper(Range & range)
  115. : m_range(range)
  116. , m_zs(boost::size(range), coord_t(0))
  117. {}
  118. range_type & get_range() { return m_range; }
  119. coord_t get_z(std::size_t i) { return m_zs[i]; }
  120. void set_z(std::size_t i, coord_t const& v) { m_zs[i] = v; }
  121. private:
  122. Range & m_range;
  123. std::vector<coord_t> m_zs;
  124. };
  125. // -----------------------------------------------------------
  126. // Boost.Geometry helpers end
  127. // -----------------------------------------------------------
  128. template <typename Par>
  129. inline typename Par::type Dx_BF(Par const& defn) { return defn.datum_params[0]; }
  130. template <typename Par>
  131. inline typename Par::type Dy_BF(Par const& defn) { return defn.datum_params[1]; }
  132. template <typename Par>
  133. inline typename Par::type Dz_BF(Par const& defn) { return defn.datum_params[2]; }
  134. template <typename Par>
  135. inline typename Par::type Rx_BF(Par const& defn) { return defn.datum_params[3]; }
  136. template <typename Par>
  137. inline typename Par::type Ry_BF(Par const& defn) { return defn.datum_params[4]; }
  138. template <typename Par>
  139. inline typename Par::type Rz_BF(Par const& defn) { return defn.datum_params[5]; }
  140. template <typename Par>
  141. inline typename Par::type M_BF(Par const& defn) { return defn.datum_params[6]; }
  142. /*
  143. ** This table is intended to indicate for any given error code in
  144. ** the range 0 to -56, whether that error will occur for all locations (ie.
  145. ** it is a problem with the coordinate system as a whole) in which case the
  146. ** value would be 0, or if the problem is with the point being transformed
  147. ** in which case the value is 1.
  148. **
  149. ** At some point we might want to move this array in with the error message
  150. ** list or something, but while experimenting with it this should be fine.
  151. **
  152. **
  153. ** NOTE (2017-10-01): Non-transient errors really should have resulted in a
  154. ** PJ==0 during initialization, and hence should be handled at the level
  155. ** before calling pj_transform. The only obvious example of the contrary
  156. ** appears to be the PJD_ERR_GRID_AREA case, which may also be taken to
  157. ** mean "no grids available"
  158. **
  159. **
  160. */
  161. static const int transient_error[60] = {
  162. /* 0 1 2 3 4 5 6 7 8 9 */
  163. /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  164. /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
  165. /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  166. /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  167. /* 40 to 49 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
  168. /* 50 to 59 */ 1, 0, 1, 0, 1, 1, 1, 1, 0, 0 };
  169. template <typename T, typename Range>
  170. inline int pj_geocentric_to_geodetic( T const& a, T const& es,
  171. Range & range );
  172. template <typename T, typename Range>
  173. inline int pj_geodetic_to_geocentric( T const& a, T const& es,
  174. Range & range );
  175. /************************************************************************/
  176. /* pj_transform() */
  177. /* */
  178. /* Currently this function doesn't recognise if two projections */
  179. /* are identical (to short circuit reprojection) because it is */
  180. /* difficult to compare PJ structures (since there are some */
  181. /* projection specific components). */
  182. /************************************************************************/
  183. template <
  184. typename SrcPrj,
  185. typename DstPrj2,
  186. typename Par,
  187. typename Range,
  188. typename Grids
  189. >
  190. inline bool pj_transform(SrcPrj const& srcprj, Par const& srcdefn,
  191. DstPrj2 const& dstprj, Par const& dstdefn,
  192. Range & range,
  193. Grids const& srcgrids,
  194. Grids const& dstgrids)
  195. {
  196. typedef typename boost::range_value<Range>::type point_type;
  197. typedef typename coordinate_type<point_type>::type coord_t;
  198. static const std::size_t dimension = geometry::dimension<point_type>::value;
  199. std::size_t point_count = boost::size(range);
  200. bool result = true;
  201. /* -------------------------------------------------------------------- */
  202. /* Transform unusual input coordinate axis orientation to */
  203. /* standard form if needed. */
  204. /* -------------------------------------------------------------------- */
  205. // Ignored
  206. /* -------------------------------------------------------------------- */
  207. /* Transform Z to meters if it isn't already. */
  208. /* -------------------------------------------------------------------- */
  209. if( srcdefn.vto_meter != 1.0 && dimension > 2 )
  210. {
  211. for( std::size_t i = 0; i < point_count; i++ )
  212. {
  213. point_type & point = geometry::range::at(range, i);
  214. set_z(point, get_z(point) * srcdefn.vto_meter);
  215. }
  216. }
  217. /* -------------------------------------------------------------------- */
  218. /* Transform geocentric source coordinates to lat/long. */
  219. /* -------------------------------------------------------------------- */
  220. if( srcdefn.is_geocent )
  221. {
  222. // Point should be cartesian 3D (ECEF)
  223. if (dimension < 3)
  224. BOOST_THROW_EXCEPTION( projection_exception(error_geocentric) );
  225. //return PJD_ERR_GEOCENTRIC;
  226. if( srcdefn.to_meter != 1.0 )
  227. {
  228. for(std::size_t i = 0; i < point_count; i++ )
  229. {
  230. point_type & point = range::at(range, i);
  231. if( ! is_invalid_point(point) )
  232. {
  233. set<0>(point, get<0>(point) * srcdefn.to_meter);
  234. set<1>(point, get<1>(point) * srcdefn.to_meter);
  235. }
  236. }
  237. }
  238. range_wrapper<Range, false> rng(range);
  239. int err = pj_geocentric_to_geodetic( srcdefn.a_orig, srcdefn.es_orig,
  240. rng );
  241. if( err != 0 )
  242. BOOST_THROW_EXCEPTION( projection_exception(err) );
  243. //return err;
  244. // NOTE: here 3D cartesian ECEF is converted into 3D geodetic LLH
  245. }
  246. /* -------------------------------------------------------------------- */
  247. /* Transform source points to lat/long, if they aren't */
  248. /* already. */
  249. /* -------------------------------------------------------------------- */
  250. else if( !srcdefn.is_latlong )
  251. {
  252. // Point should be cartesian 2D or 3D (map projection)
  253. /* Check first if projection is invertible. */
  254. /*if( (srcdefn->inv3d == NULL) && (srcdefn->inv == NULL))
  255. {
  256. pj_ctx_set_errno( pj_get_ctx(srcdefn), -17 );
  257. pj_log( pj_get_ctx(srcdefn), PJ_LOG_ERROR,
  258. "pj_transform(): source projection not invertible" );
  259. return -17;
  260. }*/
  261. /* If invertible - First try inv3d if defined */
  262. //if (srcdefn->inv3d != NULL)
  263. //{
  264. // /* Three dimensions must be defined */
  265. // if ( z == NULL)
  266. // {
  267. // pj_ctx_set_errno( pj_get_ctx(srcdefn), PJD_ERR_GEOCENTRIC);
  268. // return PJD_ERR_GEOCENTRIC;
  269. // }
  270. // for (i=0; i < point_count; i++)
  271. // {
  272. // XYZ projected_loc;
  273. // XYZ geodetic_loc;
  274. // projected_loc.u = x[point_offset*i];
  275. // projected_loc.v = y[point_offset*i];
  276. // projected_loc.w = z[point_offset*i];
  277. // if (projected_loc.u == HUGE_VAL)
  278. // continue;
  279. // geodetic_loc = pj_inv3d(projected_loc, srcdefn);
  280. // if( srcdefn->ctx->last_errno != 0 )
  281. // {
  282. // if( (srcdefn->ctx->last_errno != 33 /*EDOM*/
  283. // && srcdefn->ctx->last_errno != 34 /*ERANGE*/ )
  284. // && (srcdefn->ctx->last_errno > 0
  285. // || srcdefn->ctx->last_errno < -44 || point_count == 1
  286. // || transient_error[-srcdefn->ctx->last_errno] == 0 ) )
  287. // return srcdefn->ctx->last_errno;
  288. // else
  289. // {
  290. // geodetic_loc.u = HUGE_VAL;
  291. // geodetic_loc.v = HUGE_VAL;
  292. // geodetic_loc.w = HUGE_VAL;
  293. // }
  294. // }
  295. // x[point_offset*i] = geodetic_loc.u;
  296. // y[point_offset*i] = geodetic_loc.v;
  297. // z[point_offset*i] = geodetic_loc.w;
  298. // }
  299. //}
  300. //else
  301. {
  302. /* Fallback to the original PROJ.4 API 2d inversion - inv */
  303. for( std::size_t i = 0; i < point_count; i++ )
  304. {
  305. point_type & point = range::at(range, i);
  306. if( is_invalid_point(point) )
  307. continue;
  308. try
  309. {
  310. pj_inv(srcprj, srcdefn, point, point);
  311. }
  312. catch(projection_exception const& e)
  313. {
  314. if( (e.code() != 33 /*EDOM*/
  315. && e.code() != 34 /*ERANGE*/ )
  316. && (e.code() > 0
  317. || e.code() < -44 /*|| point_count == 1*/
  318. || transient_error[-e.code()] == 0) ) {
  319. BOOST_RETHROW
  320. } else {
  321. set_invalid_point(point);
  322. result = false;
  323. if (point_count == 1)
  324. return result;
  325. }
  326. }
  327. }
  328. }
  329. }
  330. /* -------------------------------------------------------------------- */
  331. /* But if they are already lat long, adjust for the prime */
  332. /* meridian if there is one in effect. */
  333. /* -------------------------------------------------------------------- */
  334. if( srcdefn.from_greenwich != 0.0 )
  335. {
  336. for( std::size_t i = 0; i < point_count; i++ )
  337. {
  338. point_type & point = range::at(range, i);
  339. if( ! is_invalid_point(point) )
  340. set<0>(point, get<0>(point) + srcdefn.from_greenwich);
  341. }
  342. }
  343. /* -------------------------------------------------------------------- */
  344. /* Do we need to translate from geoid to ellipsoidal vertical */
  345. /* datum? */
  346. /* -------------------------------------------------------------------- */
  347. /*if( srcdefn->has_geoid_vgrids && z != NULL )
  348. {
  349. if( pj_apply_vgridshift( srcdefn, "sgeoidgrids",
  350. &(srcdefn->vgridlist_geoid),
  351. &(srcdefn->vgridlist_geoid_count),
  352. 0, point_count, point_offset, x, y, z ) != 0 )
  353. return pj_ctx_get_errno(srcdefn->ctx);
  354. }*/
  355. /* -------------------------------------------------------------------- */
  356. /* Convert datums if needed, and possible. */
  357. /* -------------------------------------------------------------------- */
  358. if ( ! pj_datum_transform( srcdefn, dstdefn, range, srcgrids, dstgrids ) )
  359. {
  360. result = false;
  361. }
  362. /* -------------------------------------------------------------------- */
  363. /* Do we need to translate from ellipsoidal to geoid vertical */
  364. /* datum? */
  365. /* -------------------------------------------------------------------- */
  366. /*if( dstdefn->has_geoid_vgrids && z != NULL )
  367. {
  368. if( pj_apply_vgridshift( dstdefn, "sgeoidgrids",
  369. &(dstdefn->vgridlist_geoid),
  370. &(dstdefn->vgridlist_geoid_count),
  371. 1, point_count, point_offset, x, y, z ) != 0 )
  372. return dstdefn->ctx->last_errno;
  373. }*/
  374. /* -------------------------------------------------------------------- */
  375. /* But if they are staying lat long, adjust for the prime */
  376. /* meridian if there is one in effect. */
  377. /* -------------------------------------------------------------------- */
  378. if( dstdefn.from_greenwich != 0.0 )
  379. {
  380. for( std::size_t i = 0; i < point_count; i++ )
  381. {
  382. point_type & point = range::at(range, i);
  383. if( ! is_invalid_point(point) )
  384. set<0>(point, get<0>(point) - dstdefn.from_greenwich);
  385. }
  386. }
  387. /* -------------------------------------------------------------------- */
  388. /* Transform destination latlong to geocentric if required. */
  389. /* -------------------------------------------------------------------- */
  390. if( dstdefn.is_geocent )
  391. {
  392. // Point should be cartesian 3D (ECEF)
  393. if (dimension < 3)
  394. BOOST_THROW_EXCEPTION( projection_exception(error_geocentric) );
  395. //return PJD_ERR_GEOCENTRIC;
  396. // NOTE: In the original code the return value of the following
  397. // function is not checked
  398. range_wrapper<Range, false> rng(range);
  399. int err = pj_geodetic_to_geocentric( dstdefn.a_orig, dstdefn.es_orig,
  400. rng );
  401. if( err == -14 )
  402. result = false;
  403. else
  404. BOOST_THROW_EXCEPTION( projection_exception(err) );
  405. if( dstdefn.fr_meter != 1.0 )
  406. {
  407. for( std::size_t i = 0; i < point_count; i++ )
  408. {
  409. point_type & point = range::at(range, i);
  410. if( ! is_invalid_point(point) )
  411. {
  412. set<0>(point, get<0>(point) * dstdefn.fr_meter);
  413. set<1>(point, get<1>(point) * dstdefn.fr_meter);
  414. }
  415. }
  416. }
  417. }
  418. /* -------------------------------------------------------------------- */
  419. /* Transform destination points to projection coordinates, if */
  420. /* desired. */
  421. /* -------------------------------------------------------------------- */
  422. else if( !dstdefn.is_latlong )
  423. {
  424. //if( dstdefn->fwd3d != NULL)
  425. //{
  426. // for( i = 0; i < point_count; i++ )
  427. // {
  428. // XYZ projected_loc;
  429. // LPZ geodetic_loc;
  430. // geodetic_loc.u = x[point_offset*i];
  431. // geodetic_loc.v = y[point_offset*i];
  432. // geodetic_loc.w = z[point_offset*i];
  433. // if (geodetic_loc.u == HUGE_VAL)
  434. // continue;
  435. // projected_loc = pj_fwd3d( geodetic_loc, dstdefn);
  436. // if( dstdefn->ctx->last_errno != 0 )
  437. // {
  438. // if( (dstdefn->ctx->last_errno != 33 /*EDOM*/
  439. // && dstdefn->ctx->last_errno != 34 /*ERANGE*/ )
  440. // && (dstdefn->ctx->last_errno > 0
  441. // || dstdefn->ctx->last_errno < -44 || point_count == 1
  442. // || transient_error[-dstdefn->ctx->last_errno] == 0 ) )
  443. // return dstdefn->ctx->last_errno;
  444. // else
  445. // {
  446. // projected_loc.u = HUGE_VAL;
  447. // projected_loc.v = HUGE_VAL;
  448. // projected_loc.w = HUGE_VAL;
  449. // }
  450. // }
  451. // x[point_offset*i] = projected_loc.u;
  452. // y[point_offset*i] = projected_loc.v;
  453. // z[point_offset*i] = projected_loc.w;
  454. // }
  455. //}
  456. //else
  457. {
  458. for(std::size_t i = 0; i < point_count; i++ )
  459. {
  460. point_type & point = range::at(range, i);
  461. if( is_invalid_point(point) )
  462. continue;
  463. try {
  464. pj_fwd(dstprj, dstdefn, point, point);
  465. } catch (projection_exception const& e) {
  466. if( (e.code() != 33 /*EDOM*/
  467. && e.code() != 34 /*ERANGE*/ )
  468. && (e.code() > 0
  469. || e.code() < -44 /*|| point_count == 1*/
  470. || transient_error[-e.code()] == 0) ) {
  471. BOOST_RETHROW
  472. } else {
  473. set_invalid_point(point);
  474. result = false;
  475. if (point_count == 1)
  476. return result;
  477. }
  478. }
  479. }
  480. }
  481. }
  482. /* -------------------------------------------------------------------- */
  483. /* If a wrapping center other than 0 is provided, rewrap around */
  484. /* the suggested center (for latlong coordinate systems only). */
  485. /* -------------------------------------------------------------------- */
  486. else if( dstdefn.is_latlong && dstdefn.is_long_wrap_set )
  487. {
  488. for( std::size_t i = 0; i < point_count; i++ )
  489. {
  490. point_type & point = range::at(range, i);
  491. coord_t x = get_as_radian<0>(point);
  492. if( is_invalid_point(point) )
  493. continue;
  494. // TODO - units-dependant constants could be used instead
  495. while( x < dstdefn.long_wrap_center - math::pi<coord_t>() )
  496. x += math::two_pi<coord_t>();
  497. while( x > dstdefn.long_wrap_center + math::pi<coord_t>() )
  498. x -= math::two_pi<coord_t>();
  499. set_from_radian<0>(point, x);
  500. }
  501. }
  502. /* -------------------------------------------------------------------- */
  503. /* Transform Z from meters if needed. */
  504. /* -------------------------------------------------------------------- */
  505. if( dstdefn.vto_meter != 1.0 && dimension > 2 )
  506. {
  507. for( std::size_t i = 0; i < point_count; i++ )
  508. {
  509. point_type & point = geometry::range::at(range, i);
  510. set_z(point, get_z(point) * dstdefn.vfr_meter);
  511. }
  512. }
  513. /* -------------------------------------------------------------------- */
  514. /* Transform normalized axes into unusual output coordinate axis */
  515. /* orientation if needed. */
  516. /* -------------------------------------------------------------------- */
  517. // Ignored
  518. return result;
  519. }
  520. /************************************************************************/
  521. /* pj_geodetic_to_geocentric() */
  522. /************************************************************************/
  523. template <typename T, typename Range, bool AddZ>
  524. inline int pj_geodetic_to_geocentric( T const& a, T const& es,
  525. range_wrapper<Range, AddZ> & range_wrapper )
  526. {
  527. //typedef typename boost::range_iterator<Range>::type iterator;
  528. typedef typename boost::range_value<Range>::type point_type;
  529. //typedef typename coordinate_type<point_type>::type coord_t;
  530. Range & rng = range_wrapper.get_range();
  531. std::size_t point_count = boost::size(rng);
  532. int ret_errno = 0;
  533. T const b = (es == 0.0) ? a : a * sqrt(1-es);
  534. GeocentricInfo<T> gi;
  535. if( pj_Set_Geocentric_Parameters( gi, a, b ) != 0 )
  536. {
  537. return error_geocentric;
  538. }
  539. for( std::size_t i = 0 ; i < point_count ; ++i )
  540. {
  541. point_type & point = range::at(rng, i);
  542. if( is_invalid_point(point) )
  543. continue;
  544. T X = 0, Y = 0, Z = 0;
  545. if( pj_Convert_Geodetic_To_Geocentric( gi,
  546. get_as_radian<0>(point),
  547. get_as_radian<1>(point),
  548. range_wrapper.get_z(i), // Height
  549. X, Y, Z ) != 0 )
  550. {
  551. ret_errno = error_lat_or_lon_exceed_limit;
  552. set_invalid_point(point);
  553. /* but keep processing points! */
  554. }
  555. else
  556. {
  557. set<0>(point, X);
  558. set<1>(point, Y);
  559. range_wrapper.set_z(i, Z);
  560. }
  561. }
  562. return ret_errno;
  563. }
  564. /************************************************************************/
  565. /* pj_geodetic_to_geocentric() */
  566. /************************************************************************/
  567. template <typename T, typename Range, bool AddZ>
  568. inline int pj_geocentric_to_geodetic( T const& a, T const& es,
  569. range_wrapper<Range, AddZ> & range_wrapper )
  570. {
  571. //typedef typename boost::range_iterator<Range>::type iterator;
  572. typedef typename boost::range_value<Range>::type point_type;
  573. //typedef typename coordinate_type<point_type>::type coord_t;
  574. Range & rng = range_wrapper.get_range();
  575. std::size_t point_count = boost::size(rng);
  576. T const b = (es == 0.0) ? a : a * sqrt(1-es);
  577. GeocentricInfo<T> gi;
  578. if( pj_Set_Geocentric_Parameters( gi, a, b ) != 0 )
  579. {
  580. return error_geocentric;
  581. }
  582. for( std::size_t i = 0 ; i < point_count ; ++i )
  583. {
  584. point_type & point = range::at(rng, i);
  585. if( is_invalid_point(point) )
  586. continue;
  587. T Longitude = 0, Latitude = 0, Height = 0;
  588. pj_Convert_Geocentric_To_Geodetic( gi,
  589. get<0>(point),
  590. get<1>(point),
  591. range_wrapper.get_z(i), // z
  592. Longitude, Latitude, Height );
  593. set_from_radian<0>(point, Longitude);
  594. set_from_radian<1>(point, Latitude);
  595. range_wrapper.set_z(i, Height); // Height
  596. }
  597. return 0;
  598. }
  599. /************************************************************************/
  600. /* pj_compare_datums() */
  601. /* */
  602. /* Returns TRUE if the two datums are identical, otherwise */
  603. /* FALSE. */
  604. /************************************************************************/
  605. template <typename Par>
  606. inline bool pj_compare_datums( Par & srcdefn, Par & dstdefn )
  607. {
  608. if( srcdefn.datum_type != dstdefn.datum_type )
  609. {
  610. return false;
  611. }
  612. else if( srcdefn.a_orig != dstdefn.a_orig
  613. || math::abs(srcdefn.es_orig - dstdefn.es_orig) > 0.000000000050 )
  614. {
  615. /* the tolerance for es is to ensure that GRS80 and WGS84 are
  616. considered identical */
  617. return false;
  618. }
  619. else if( srcdefn.datum_type == datum_3param )
  620. {
  621. return (srcdefn.datum_params[0] == dstdefn.datum_params[0]
  622. && srcdefn.datum_params[1] == dstdefn.datum_params[1]
  623. && srcdefn.datum_params[2] == dstdefn.datum_params[2]);
  624. }
  625. else if( srcdefn.datum_type == datum_7param )
  626. {
  627. return (srcdefn.datum_params[0] == dstdefn.datum_params[0]
  628. && srcdefn.datum_params[1] == dstdefn.datum_params[1]
  629. && srcdefn.datum_params[2] == dstdefn.datum_params[2]
  630. && srcdefn.datum_params[3] == dstdefn.datum_params[3]
  631. && srcdefn.datum_params[4] == dstdefn.datum_params[4]
  632. && srcdefn.datum_params[5] == dstdefn.datum_params[5]
  633. && srcdefn.datum_params[6] == dstdefn.datum_params[6]);
  634. }
  635. else if( srcdefn.datum_type == datum_gridshift )
  636. {
  637. return srcdefn.nadgrids == dstdefn.nadgrids;
  638. }
  639. else
  640. return true;
  641. }
  642. /************************************************************************/
  643. /* pj_geocentic_to_wgs84() */
  644. /************************************************************************/
  645. template <typename Par, typename Range, bool AddZ>
  646. inline int pj_geocentric_to_wgs84( Par const& defn,
  647. range_wrapper<Range, AddZ> & range_wrapper )
  648. {
  649. typedef typename boost::range_value<Range>::type point_type;
  650. typedef typename coordinate_type<point_type>::type coord_t;
  651. Range & rng = range_wrapper.get_range();
  652. std::size_t point_count = boost::size(rng);
  653. if( defn.datum_type == datum_3param )
  654. {
  655. for(std::size_t i = 0; i < point_count; i++ )
  656. {
  657. point_type & point = range::at(rng, i);
  658. if( is_invalid_point(point) )
  659. continue;
  660. set<0>(point, get<0>(point) + Dx_BF(defn));
  661. set<1>(point, get<1>(point) + Dy_BF(defn));
  662. range_wrapper.set_z(i, range_wrapper.get_z(i) + Dz_BF(defn));
  663. }
  664. }
  665. else if( defn.datum_type == datum_7param )
  666. {
  667. for(std::size_t i = 0; i < point_count; i++ )
  668. {
  669. point_type & point = range::at(rng, i);
  670. if( is_invalid_point(point) )
  671. continue;
  672. coord_t x = get<0>(point);
  673. coord_t y = get<1>(point);
  674. coord_t z = range_wrapper.get_z(i);
  675. coord_t x_out, y_out, z_out;
  676. x_out = M_BF(defn)*( x - Rz_BF(defn)*y + Ry_BF(defn)*z) + Dx_BF(defn);
  677. y_out = M_BF(defn)*( Rz_BF(defn)*x + y - Rx_BF(defn)*z) + Dy_BF(defn);
  678. z_out = M_BF(defn)*(-Ry_BF(defn)*x + Rx_BF(defn)*y + z) + Dz_BF(defn);
  679. set<0>(point, x_out);
  680. set<1>(point, y_out);
  681. range_wrapper.set_z(i, z_out);
  682. }
  683. }
  684. return 0;
  685. }
  686. /************************************************************************/
  687. /* pj_geocentic_from_wgs84() */
  688. /************************************************************************/
  689. template <typename Par, typename Range, bool AddZ>
  690. inline int pj_geocentric_from_wgs84( Par const& defn,
  691. range_wrapper<Range, AddZ> & range_wrapper )
  692. {
  693. typedef typename boost::range_value<Range>::type point_type;
  694. typedef typename coordinate_type<point_type>::type coord_t;
  695. Range & rng = range_wrapper.get_range();
  696. std::size_t point_count = boost::size(rng);
  697. if( defn.datum_type == datum_3param )
  698. {
  699. for(std::size_t i = 0; i < point_count; i++ )
  700. {
  701. point_type & point = range::at(rng, i);
  702. if( is_invalid_point(point) )
  703. continue;
  704. set<0>(point, get<0>(point) - Dx_BF(defn));
  705. set<1>(point, get<1>(point) - Dy_BF(defn));
  706. range_wrapper.set_z(i, range_wrapper.get_z(i) - Dz_BF(defn));
  707. }
  708. }
  709. else if( defn.datum_type == datum_7param )
  710. {
  711. for(std::size_t i = 0; i < point_count; i++ )
  712. {
  713. point_type & point = range::at(rng, i);
  714. if( is_invalid_point(point) )
  715. continue;
  716. coord_t x = get<0>(point);
  717. coord_t y = get<1>(point);
  718. coord_t z = range_wrapper.get_z(i);
  719. coord_t x_tmp = (x - Dx_BF(defn)) / M_BF(defn);
  720. coord_t y_tmp = (y - Dy_BF(defn)) / M_BF(defn);
  721. coord_t z_tmp = (z - Dz_BF(defn)) / M_BF(defn);
  722. x = x_tmp + Rz_BF(defn)*y_tmp - Ry_BF(defn)*z_tmp;
  723. y = -Rz_BF(defn)*x_tmp + y_tmp + Rx_BF(defn)*z_tmp;
  724. z = Ry_BF(defn)*x_tmp - Rx_BF(defn)*y_tmp + z_tmp;
  725. set<0>(point, x);
  726. set<1>(point, y);
  727. range_wrapper.set_z(i, z);
  728. }
  729. }
  730. return 0;
  731. }
  732. inline bool pj_datum_check_error(int err)
  733. {
  734. return err != 0 && (err > 0 || transient_error[-err] == 0);
  735. }
  736. /************************************************************************/
  737. /* pj_datum_transform() */
  738. /* */
  739. /* The input should be long/lat/z coordinates in radians in the */
  740. /* source datum, and the output should be long/lat/z */
  741. /* coordinates in radians in the destination datum. */
  742. /************************************************************************/
  743. template <typename Par, typename Range, typename Grids>
  744. inline bool pj_datum_transform(Par const& srcdefn,
  745. Par const& dstdefn,
  746. Range & range,
  747. Grids const& srcgrids,
  748. Grids const& dstgrids)
  749. {
  750. typedef typename Par::type calc_t;
  751. // This has to be consistent with default spheroid and pj_ellps
  752. // TODO: Define in one place
  753. static const calc_t wgs84_a = 6378137.0;
  754. static const calc_t wgs84_b = 6356752.3142451793;
  755. static const calc_t wgs84_es = 1. - (wgs84_b * wgs84_b) / (wgs84_a * wgs84_a);
  756. bool result = true;
  757. calc_t src_a, src_es, dst_a, dst_es;
  758. /* -------------------------------------------------------------------- */
  759. /* We cannot do any meaningful datum transformation if either */
  760. /* the source or destination are of an unknown datum type */
  761. /* (ie. only a +ellps declaration, no +datum). This is new */
  762. /* behavior for PROJ 4.6.0. */
  763. /* -------------------------------------------------------------------- */
  764. if( srcdefn.datum_type == datum_unknown
  765. || dstdefn.datum_type == datum_unknown )
  766. return result;
  767. /* -------------------------------------------------------------------- */
  768. /* Short cut if the datums are identical. */
  769. /* -------------------------------------------------------------------- */
  770. if( pj_compare_datums( srcdefn, dstdefn ) )
  771. return result;
  772. src_a = srcdefn.a_orig;
  773. src_es = srcdefn.es_orig;
  774. dst_a = dstdefn.a_orig;
  775. dst_es = dstdefn.es_orig;
  776. /* -------------------------------------------------------------------- */
  777. /* Create a temporary Z array if one is not provided. */
  778. /* -------------------------------------------------------------------- */
  779. range_wrapper<Range> z_range(range);
  780. /* -------------------------------------------------------------------- */
  781. /* If this datum requires grid shifts, then apply it to geodetic */
  782. /* coordinates. */
  783. /* -------------------------------------------------------------------- */
  784. if( srcdefn.datum_type == datum_gridshift )
  785. {
  786. try {
  787. pj_apply_gridshift_2<false>( srcdefn, range, srcgrids );
  788. } catch (projection_exception const& e) {
  789. if (pj_datum_check_error(e.code())) {
  790. BOOST_RETHROW
  791. }
  792. }
  793. src_a = wgs84_a;
  794. src_es = wgs84_es;
  795. }
  796. if( dstdefn.datum_type == datum_gridshift )
  797. {
  798. dst_a = wgs84_a;
  799. dst_es = wgs84_es;
  800. }
  801. /* ==================================================================== */
  802. /* Do we need to go through geocentric coordinates? */
  803. /* ==================================================================== */
  804. if( src_es != dst_es || src_a != dst_a
  805. || srcdefn.datum_type == datum_3param
  806. || srcdefn.datum_type == datum_7param
  807. || dstdefn.datum_type == datum_3param
  808. || dstdefn.datum_type == datum_7param)
  809. {
  810. /* -------------------------------------------------------------------- */
  811. /* Convert to geocentric coordinates. */
  812. /* -------------------------------------------------------------------- */
  813. int err = pj_geodetic_to_geocentric( src_a, src_es, z_range );
  814. if (pj_datum_check_error(err))
  815. BOOST_THROW_EXCEPTION( projection_exception(err) );
  816. else if (err != 0)
  817. result = false;
  818. /* -------------------------------------------------------------------- */
  819. /* Convert between datums. */
  820. /* -------------------------------------------------------------------- */
  821. if( srcdefn.datum_type == datum_3param
  822. || srcdefn.datum_type == datum_7param )
  823. {
  824. try {
  825. pj_geocentric_to_wgs84( srcdefn, z_range );
  826. } catch (projection_exception const& e) {
  827. if (pj_datum_check_error(e.code())) {
  828. BOOST_RETHROW
  829. }
  830. }
  831. }
  832. if( dstdefn.datum_type == datum_3param
  833. || dstdefn.datum_type == datum_7param )
  834. {
  835. try {
  836. pj_geocentric_from_wgs84( dstdefn, z_range );
  837. } catch (projection_exception const& e) {
  838. if (pj_datum_check_error(e.code())) {
  839. BOOST_RETHROW
  840. }
  841. }
  842. }
  843. /* -------------------------------------------------------------------- */
  844. /* Convert back to geodetic coordinates. */
  845. /* -------------------------------------------------------------------- */
  846. err = pj_geocentric_to_geodetic( dst_a, dst_es, z_range );
  847. if (pj_datum_check_error(err))
  848. BOOST_THROW_EXCEPTION( projection_exception(err) );
  849. else if (err != 0)
  850. result = false;
  851. }
  852. /* -------------------------------------------------------------------- */
  853. /* Apply grid shift to destination if required. */
  854. /* -------------------------------------------------------------------- */
  855. if( dstdefn.datum_type == datum_gridshift )
  856. {
  857. try {
  858. pj_apply_gridshift_2<true>( dstdefn, range, dstgrids );
  859. } catch (projection_exception const& e) {
  860. if (pj_datum_check_error(e.code()))
  861. BOOST_RETHROW
  862. }
  863. }
  864. return result;
  865. }
  866. } // namespace detail
  867. }}} // namespace boost::geometry::projections
  868. #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_TRANSFORM_HPP