pj_apply_gridshift.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. // Boost.Geometry
  2. // This file is manually converted from PROJ4
  3. // This file was modified by Oracle on 2018, 2019.
  4. // Modifications copyright (c) 2018-2019, 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. // Author: Frank Warmerdam, warmerdam@pobox.com
  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_APPLY_GRIDSHIFT_HPP
  32. #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP
  33. #include <boost/geometry/core/assert.hpp>
  34. #include <boost/geometry/core/radian_access.hpp>
  35. #include <boost/geometry/srs/projections/impl/adjlon.hpp>
  36. #include <boost/geometry/srs/projections/impl/function_overloads.hpp>
  37. #include <boost/geometry/srs/projections/impl/pj_gridlist.hpp>
  38. #include <boost/geometry/util/range.hpp>
  39. namespace boost { namespace geometry { namespace projections
  40. {
  41. namespace detail
  42. {
  43. // Originally implemented in nad_intr.c
  44. template <typename CalcT>
  45. inline void nad_intr(CalcT in_lon, CalcT in_lat,
  46. CalcT & out_lon, CalcT & out_lat,
  47. pj_ctable const& ct)
  48. {
  49. pj_ctable::lp_t frct;
  50. pj_ctable::ilp_t indx;
  51. boost::int32_t in;
  52. indx.lam = int_floor(in_lon /= ct.del.lam);
  53. indx.phi = int_floor(in_lat /= ct.del.phi);
  54. frct.lam = in_lon - indx.lam;
  55. frct.phi = in_lat - indx.phi;
  56. // TODO: implement differently
  57. out_lon = out_lat = HUGE_VAL;
  58. if (indx.lam < 0) {
  59. if (indx.lam == -1 && frct.lam > 0.99999999999) {
  60. ++indx.lam;
  61. frct.lam = 0.;
  62. } else
  63. return;
  64. } else if ((in = indx.lam + 1) >= ct.lim.lam) {
  65. if (in == ct.lim.lam && frct.lam < 1e-11) {
  66. --indx.lam;
  67. frct.lam = 1.;
  68. } else
  69. return;
  70. }
  71. if (indx.phi < 0) {
  72. if (indx.phi == -1 && frct.phi > 0.99999999999) {
  73. ++indx.phi;
  74. frct.phi = 0.;
  75. } else
  76. return;
  77. } else if ((in = indx.phi + 1) >= ct.lim.phi) {
  78. if (in == ct.lim.phi && frct.phi < 1e-11) {
  79. --indx.phi;
  80. frct.phi = 1.;
  81. } else
  82. return;
  83. }
  84. boost::int32_t index = indx.phi * ct.lim.lam + indx.lam;
  85. pj_ctable::flp_t const& f00 = ct.cvs[index++];
  86. pj_ctable::flp_t const& f10 = ct.cvs[index];
  87. index += ct.lim.lam;
  88. pj_ctable::flp_t const& f11 = ct.cvs[index--];
  89. pj_ctable::flp_t const& f01 = ct.cvs[index];
  90. CalcT m00, m10, m01, m11;
  91. m11 = m10 = frct.lam;
  92. m00 = m01 = 1. - frct.lam;
  93. m11 *= frct.phi;
  94. m01 *= frct.phi;
  95. frct.phi = 1. - frct.phi;
  96. m00 *= frct.phi;
  97. m10 *= frct.phi;
  98. out_lon = m00 * f00.lam + m10 * f10.lam +
  99. m01 * f01.lam + m11 * f11.lam;
  100. out_lat = m00 * f00.phi + m10 * f10.phi +
  101. m01 * f01.phi + m11 * f11.phi;
  102. }
  103. // Originally implemented in nad_cvt.c
  104. template <bool Inverse, typename CalcT>
  105. inline void nad_cvt(CalcT const& in_lon, CalcT const& in_lat,
  106. CalcT & out_lon, CalcT & out_lat,
  107. pj_gi const& gi)
  108. {
  109. static const int max_iterations = 10;
  110. static const CalcT tol = 1e-12;
  111. static const CalcT toltol = tol * tol;
  112. static const CalcT pi = math::pi<CalcT>();
  113. // horizontal grid expected
  114. BOOST_GEOMETRY_ASSERT_MSG(gi.format != pj_gi::gtx,
  115. "Vertical grid cannot be used in horizontal shift.");
  116. pj_ctable const& ct = gi.ct;
  117. // TODO: implement differently
  118. if (in_lon == HUGE_VAL)
  119. {
  120. out_lon = HUGE_VAL;
  121. out_lat = HUGE_VAL;
  122. return;
  123. }
  124. // normalize input to ll origin
  125. pj_ctable::lp_t tb;
  126. tb.lam = in_lon - ct.ll.lam;
  127. tb.phi = in_lat - ct.ll.phi;
  128. tb.lam = adjlon (tb.lam - pi) + pi;
  129. pj_ctable::lp_t t;
  130. nad_intr(tb.lam, tb.phi, t.lam, t.phi, ct);
  131. if (t.lam == HUGE_VAL)
  132. {
  133. out_lon = HUGE_VAL;
  134. out_lat = HUGE_VAL;
  135. return;
  136. }
  137. if (! Inverse)
  138. {
  139. out_lon = in_lon - t.lam;
  140. out_lat = in_lat - t.phi;
  141. return;
  142. }
  143. t.lam = tb.lam + t.lam;
  144. t.phi = tb.phi - t.phi;
  145. int i = max_iterations;
  146. pj_ctable::lp_t del, dif;
  147. do
  148. {
  149. nad_intr(t.lam, t.phi, del.lam, del.phi, ct);
  150. // This case used to return failure, but I have
  151. // changed it to return the first order approximation
  152. // of the inverse shift. This avoids cases where the
  153. // grid shift *into* this grid came from another grid.
  154. // While we aren't returning optimally correct results
  155. // I feel a close result in this case is better than
  156. // no result. NFW
  157. // To demonstrate use -112.5839956 49.4914451 against
  158. // the NTv2 grid shift file from Canada.
  159. if (del.lam == HUGE_VAL)
  160. {
  161. // Inverse grid shift iteration failed, presumably at grid edge. Using first approximation.
  162. break;
  163. }
  164. dif.lam = t.lam - del.lam - tb.lam;
  165. dif.phi = t.phi + del.phi - tb.phi;
  166. t.lam -= dif.lam;
  167. t.phi -= dif.phi;
  168. }
  169. while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); // prob. slightly faster than hypot()
  170. if (i==0)
  171. {
  172. // Inverse grid shift iterator failed to converge.
  173. out_lon = HUGE_VAL;
  174. out_lat = HUGE_VAL;
  175. return;
  176. }
  177. out_lon = adjlon (t.lam + ct.ll.lam);
  178. out_lat = t.phi + ct.ll.phi;
  179. }
  180. /************************************************************************/
  181. /* find_grid() */
  182. /* */
  183. /* Determine which grid is the correct given an input coordinate. */
  184. /************************************************************************/
  185. // Originally find_ctable()
  186. // here divided into grid_disjoint(), find_grid() and load_grid()
  187. template <typename T>
  188. inline bool grid_disjoint(T const& lam, T const& phi,
  189. pj_ctable const& ct)
  190. {
  191. double epsilon = (fabs(ct.del.phi)+fabs(ct.del.lam))/10000.0;
  192. return ct.ll.phi - epsilon > phi
  193. || ct.ll.lam - epsilon > lam
  194. || (ct.ll.phi + (ct.lim.phi-1) * ct.del.phi + epsilon < phi)
  195. || (ct.ll.lam + (ct.lim.lam-1) * ct.del.lam + epsilon < lam);
  196. }
  197. template <typename T>
  198. inline pj_gi * find_grid(T const& lam,
  199. T const& phi,
  200. std::vector<pj_gi>::iterator first,
  201. std::vector<pj_gi>::iterator last)
  202. {
  203. pj_gi * gip = NULL;
  204. for( ; first != last ; ++first )
  205. {
  206. // skip tables that don't match our point at all.
  207. if (! grid_disjoint(lam, phi, first->ct))
  208. {
  209. // skip vertical grids
  210. if (first->format != pj_gi::gtx)
  211. {
  212. gip = boost::addressof(*first);
  213. break;
  214. }
  215. }
  216. }
  217. // If we didn't find a child then nothing more to do
  218. if( gip == NULL )
  219. return gip;
  220. // Otherwise use the child, first checking it's children
  221. pj_gi * child = find_grid(lam, phi, first->children.begin(), first->children.end());
  222. if (child != NULL)
  223. gip = child;
  224. return gip;
  225. }
  226. template <typename T>
  227. inline pj_gi * find_grid(T const& lam,
  228. T const& phi,
  229. pj_gridinfo & grids,
  230. std::vector<std::size_t> const& gridindexes)
  231. {
  232. pj_gi * gip = NULL;
  233. // keep trying till we find a table that works
  234. for (std::size_t i = 0 ; i < gridindexes.size() ; ++i)
  235. {
  236. pj_gi & gi = grids[gridindexes[i]];
  237. // skip tables that don't match our point at all.
  238. if (! grid_disjoint(lam, phi, gi.ct))
  239. {
  240. // skip vertical grids
  241. if (gi.format != pj_gi::gtx)
  242. {
  243. gip = boost::addressof(gi);
  244. break;
  245. }
  246. }
  247. }
  248. if (gip == NULL)
  249. return gip;
  250. // If we have child nodes, check to see if any of them apply.
  251. pj_gi * child = find_grid(lam, phi, gip->children.begin(), gip->children.end());
  252. if (child != NULL)
  253. gip = child;
  254. // if we get this far we have found a suitable grid
  255. return gip;
  256. }
  257. template <typename StreamPolicy>
  258. inline bool load_grid(StreamPolicy const& stream_policy, pj_gi_load & gi)
  259. {
  260. // load the grid shift info if we don't have it.
  261. if (gi.ct.cvs.empty())
  262. {
  263. typename StreamPolicy::stream_type is;
  264. stream_policy.open(is, gi.gridname);
  265. if (! pj_gridinfo_load(is, gi))
  266. {
  267. //pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID );
  268. return false;
  269. }
  270. }
  271. return true;
  272. }
  273. /************************************************************************/
  274. /* pj_apply_gridshift_3() */
  275. /* */
  276. /* This is the real workhorse, given a gridlist. */
  277. /************************************************************************/
  278. // Generic stream policy and standard grids
  279. template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename Grids>
  280. inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy,
  281. Range & range,
  282. Grids & grids,
  283. std::vector<std::size_t> const& gridindexes,
  284. grids_tag)
  285. {
  286. typedef typename boost::range_size<Range>::type size_type;
  287. // If the grids are empty the indexes are as well
  288. if (gridindexes.empty())
  289. {
  290. //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
  291. //return PJD_ERR_FAILED_TO_LOAD_GRID;
  292. return false;
  293. }
  294. size_type point_count = boost::size(range);
  295. for (size_type i = 0 ; i < point_count ; ++i)
  296. {
  297. typename boost::range_reference<Range>::type
  298. point = range::at(range, i);
  299. CalcT in_lon = geometry::get_as_radian<0>(point);
  300. CalcT in_lat = geometry::get_as_radian<1>(point);
  301. pj_gi * gip = find_grid(in_lon, in_lat, grids.gridinfo, gridindexes);
  302. if ( gip != NULL )
  303. {
  304. // load the grid shift info if we don't have it.
  305. if (! gip->ct.cvs.empty() || load_grid(stream_policy, *gip))
  306. {
  307. // TODO: use set_invalid_point() or similar mechanism
  308. CalcT out_lon = HUGE_VAL;
  309. CalcT out_lat = HUGE_VAL;
  310. nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip);
  311. // TODO: check differently
  312. if ( out_lon != HUGE_VAL )
  313. {
  314. geometry::set_from_radian<0>(point, out_lon);
  315. geometry::set_from_radian<1>(point, out_lat);
  316. }
  317. }
  318. }
  319. }
  320. return true;
  321. }
  322. // Generic stream policy and shared grids
  323. template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename SharedGrids>
  324. inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy,
  325. Range & range,
  326. SharedGrids & grids,
  327. std::vector<std::size_t> const& gridindexes,
  328. shared_grids_tag)
  329. {
  330. typedef typename boost::range_size<Range>::type size_type;
  331. // If the grids are empty the indexes are as well
  332. if (gridindexes.empty())
  333. {
  334. //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
  335. //return PJD_ERR_FAILED_TO_LOAD_GRID;
  336. return false;
  337. }
  338. size_type point_count = boost::size(range);
  339. // local storage
  340. pj_gi_load local_gi;
  341. for (size_type i = 0 ; i < point_count ; )
  342. {
  343. bool load_needed = false;
  344. CalcT in_lon = 0;
  345. CalcT in_lat = 0;
  346. {
  347. typename SharedGrids::read_locked lck_grids(grids);
  348. for ( ; i < point_count ; ++i )
  349. {
  350. typename boost::range_reference<Range>::type
  351. point = range::at(range, i);
  352. in_lon = geometry::get_as_radian<0>(point);
  353. in_lat = geometry::get_as_radian<1>(point);
  354. pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes);
  355. if (gip == NULL)
  356. {
  357. // do nothing
  358. }
  359. else if (! gip->ct.cvs.empty())
  360. {
  361. // TODO: use set_invalid_point() or similar mechanism
  362. CalcT out_lon = HUGE_VAL;
  363. CalcT out_lat = HUGE_VAL;
  364. nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip);
  365. // TODO: check differently
  366. if (out_lon != HUGE_VAL)
  367. {
  368. geometry::set_from_radian<0>(point, out_lon);
  369. geometry::set_from_radian<1>(point, out_lat);
  370. }
  371. }
  372. else
  373. {
  374. // loading is needed
  375. local_gi = *gip;
  376. load_needed = true;
  377. break;
  378. }
  379. }
  380. }
  381. if (load_needed)
  382. {
  383. if (load_grid(stream_policy, local_gi))
  384. {
  385. typename SharedGrids::write_locked lck_grids(grids);
  386. // check again in case other thread already loaded the grid.
  387. pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes);
  388. if (gip != NULL && gip->ct.cvs.empty())
  389. {
  390. // swap loaded local storage with empty grid
  391. local_gi.swap(*gip);
  392. }
  393. }
  394. else
  395. {
  396. ++i;
  397. }
  398. }
  399. }
  400. return true;
  401. }
  402. /************************************************************************/
  403. /* pj_apply_gridshift_2() */
  404. /* */
  405. /* This implementation uses the gridlist from a coordinate */
  406. /* system definition. If the gridlist has not yet been */
  407. /* populated in the coordinate system definition we set it up */
  408. /* now. */
  409. /************************************************************************/
  410. template <bool Inverse, typename Par, typename Range, typename ProjGrids>
  411. inline bool pj_apply_gridshift_2(Par const& /*defn*/, Range & range, ProjGrids const& proj_grids)
  412. {
  413. /*if( defn->catalog_name != NULL )
  414. return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset,
  415. x, y, z );*/
  416. /*std::vector<std::size_t> gridindexes;
  417. pj_gridlist_from_nadgrids(pj_get_param_s(defn.params, "nadgrids"),
  418. grids.storage_ptr->stream_policy,
  419. grids.storage_ptr->grids,
  420. gridindexes);*/
  421. // At this point the grids should be initialized
  422. if (proj_grids.hindexes.empty())
  423. return false;
  424. return pj_apply_gridshift_3
  425. <
  426. Inverse, typename Par::type
  427. >(proj_grids.grids_storage().stream_policy,
  428. range,
  429. proj_grids.grids_storage().hgrids,
  430. proj_grids.hindexes,
  431. typename ProjGrids::grids_storage_type::grids_type::tag());
  432. }
  433. template <bool Inverse, typename Par, typename Range>
  434. inline bool pj_apply_gridshift_2(Par const& , Range & , srs::detail::empty_projection_grids const& )
  435. {
  436. return false;
  437. }
  438. } // namespace detail
  439. }}} // namespace boost::geometry::projections
  440. #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP