pj_transform.hpp 38 KB

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