2 // This file is manually converted from PROJ4
4 // This file was modified by Oracle on 2018, 2019.
5 // Modifications copyright (c) 2018-2019, Oracle and/or its affiliates.
6 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
8 // Use, modification and distribution is subject to the Boost Software License,
9 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
10 // http://www.boost.org/LICENSE_1_0.txt)
12 // This file is converted from PROJ4, http://trac.osgeo.org/proj
13 // PROJ4 is originally written by Gerald Evenden (then of the USGS)
14 // PROJ4 is maintained by Frank Warmerdam
15 // This file was converted to Geometry Library by Adam Wulkiewicz
17 // Original copyright notice:
18 // Author: Frank Warmerdam, warmerdam@pobox.com
20 // Copyright (c) 2000, Frank Warmerdam
22 // Permission is hereby granted, free of charge, to any person obtaining a
23 // copy of this software and associated documentation files (the "Software"),
24 // to deal in the Software without restriction, including without limitation
25 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
26 // and/or sell copies of the Software, and to permit persons to whom the
27 // Software is furnished to do so, subject to the following conditions:
29 // The above copyright notice and this permission notice shall be included
30 // in all copies or substantial portions of the Software.
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
33 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
35 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
37 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
38 // DEALINGS IN THE SOFTWARE.
40 #ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP
41 #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP
44 #include <boost/geometry/core/assert.hpp>
45 #include <boost/geometry/core/radian_access.hpp>
47 #include <boost/geometry/srs/projections/impl/adjlon.hpp>
48 #include <boost/geometry/srs/projections/impl/function_overloads.hpp>
49 #include <boost/geometry/srs/projections/impl/pj_gridlist.hpp>
51 #include <boost/geometry/util/range.hpp>
54 namespace boost { namespace geometry { namespace projections
60 // Originally implemented in nad_intr.c
61 template <typename CalcT>
62 inline void nad_intr(CalcT in_lon, CalcT in_lat,
63 CalcT & out_lon, CalcT & out_lat,
67 pj_ctable::ilp_t indx;
70 indx.lam = int_floor(in_lon /= ct.del.lam);
71 indx.phi = int_floor(in_lat /= ct.del.phi);
72 frct.lam = in_lon - indx.lam;
73 frct.phi = in_lat - indx.phi;
74 // TODO: implement differently
75 out_lon = out_lat = HUGE_VAL;
77 if (indx.lam == -1 && frct.lam > 0.99999999999) {
82 } else if ((in = indx.lam + 1) >= ct.lim.lam) {
83 if (in == ct.lim.lam && frct.lam < 1e-11) {
90 if (indx.phi == -1 && frct.phi > 0.99999999999) {
95 } else if ((in = indx.phi + 1) >= ct.lim.phi) {
96 if (in == ct.lim.phi && frct.phi < 1e-11) {
102 boost::int32_t index = indx.phi * ct.lim.lam + indx.lam;
103 pj_ctable::flp_t const& f00 = ct.cvs[index++];
104 pj_ctable::flp_t const& f10 = ct.cvs[index];
106 pj_ctable::flp_t const& f11 = ct.cvs[index--];
107 pj_ctable::flp_t const& f01 = ct.cvs[index];
108 CalcT m00, m10, m01, m11;
109 m11 = m10 = frct.lam;
110 m00 = m01 = 1. - frct.lam;
113 frct.phi = 1. - frct.phi;
116 out_lon = m00 * f00.lam + m10 * f10.lam +
117 m01 * f01.lam + m11 * f11.lam;
118 out_lat = m00 * f00.phi + m10 * f10.phi +
119 m01 * f01.phi + m11 * f11.phi;
122 // Originally implemented in nad_cvt.c
123 template <bool Inverse, typename CalcT>
124 inline void nad_cvt(CalcT const& in_lon, CalcT const& in_lat,
125 CalcT & out_lon, CalcT & out_lat,
128 static const int max_iterations = 10;
129 static const CalcT tol = 1e-12;
130 static const CalcT toltol = tol * tol;
131 static const CalcT pi = math::pi<CalcT>();
133 // horizontal grid expected
134 BOOST_GEOMETRY_ASSERT_MSG(gi.format != pj_gi::gtx,
135 "Vertical grid cannot be used in horizontal shift.");
137 pj_ctable const& ct = gi.ct;
139 // TODO: implement differently
140 if (in_lon == HUGE_VAL)
147 // normalize input to ll origin
149 tb.lam = in_lon - ct.ll.lam;
150 tb.phi = in_lat - ct.ll.phi;
151 tb.lam = adjlon (tb.lam - pi) + pi;
154 nad_intr(tb.lam, tb.phi, t.lam, t.phi, ct);
155 if (t.lam == HUGE_VAL)
164 out_lon = in_lon - t.lam;
165 out_lat = in_lat - t.phi;
169 t.lam = tb.lam + t.lam;
170 t.phi = tb.phi - t.phi;
172 int i = max_iterations;
173 pj_ctable::lp_t del, dif;
176 nad_intr(t.lam, t.phi, del.lam, del.phi, ct);
178 // This case used to return failure, but I have
179 // changed it to return the first order approximation
180 // of the inverse shift. This avoids cases where the
181 // grid shift *into* this grid came from another grid.
182 // While we aren't returning optimally correct results
183 // I feel a close result in this case is better than
185 // To demonstrate use -112.5839956 49.4914451 against
186 // the NTv2 grid shift file from Canada.
187 if (del.lam == HUGE_VAL)
189 // Inverse grid shift iteration failed, presumably at grid edge. Using first approximation.
193 dif.lam = t.lam - del.lam - tb.lam;
194 dif.phi = t.phi + del.phi - tb.phi;
199 while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); // prob. slightly faster than hypot()
203 // Inverse grid shift iterator failed to converge.
209 out_lon = adjlon (t.lam + ct.ll.lam);
210 out_lat = t.phi + ct.ll.phi;
214 /************************************************************************/
217 /* Determine which grid is the correct given an input coordinate. */
218 /************************************************************************/
220 // Originally find_ctable()
221 // here divided into grid_disjoint(), find_grid() and load_grid()
223 template <typename T>
224 inline bool grid_disjoint(T const& lam, T const& phi,
227 double epsilon = (fabs(ct.del.phi)+fabs(ct.del.lam))/10000.0;
228 return ct.ll.phi - epsilon > phi
229 || ct.ll.lam - epsilon > lam
230 || (ct.ll.phi + (ct.lim.phi-1) * ct.del.phi + epsilon < phi)
231 || (ct.ll.lam + (ct.lim.lam-1) * ct.del.lam + epsilon < lam);
234 template <typename T>
235 inline pj_gi * find_grid(T const& lam,
237 std::vector<pj_gi>::iterator first,
238 std::vector<pj_gi>::iterator last)
242 for( ; first != last ; ++first )
244 // skip tables that don't match our point at all.
245 if (! grid_disjoint(lam, phi, first->ct))
247 // skip vertical grids
248 if (first->format != pj_gi::gtx)
250 gip = boost::addressof(*first);
256 // If we didn't find a child then nothing more to do
260 // Otherwise use the child, first checking it's children
261 pj_gi * child = find_grid(lam, phi, first->children.begin(), first->children.end());
268 template <typename T>
269 inline pj_gi * find_grid(T const& lam,
272 std::vector<std::size_t> const& gridindexes)
276 // keep trying till we find a table that works
277 for (std::size_t i = 0 ; i < gridindexes.size() ; ++i)
279 pj_gi & gi = grids[gridindexes[i]];
281 // skip tables that don't match our point at all.
282 if (! grid_disjoint(lam, phi, gi.ct))
284 // skip vertical grids
285 if (gi.format != pj_gi::gtx)
287 gip = boost::addressof(gi);
296 // If we have child nodes, check to see if any of them apply.
297 pj_gi * child = find_grid(lam, phi, gip->children.begin(), gip->children.end());
301 // if we get this far we have found a suitable grid
306 template <typename StreamPolicy>
307 inline bool load_grid(StreamPolicy const& stream_policy, pj_gi_load & gi)
309 // load the grid shift info if we don't have it.
310 if (gi.ct.cvs.empty())
312 typename StreamPolicy::stream_type is;
313 stream_policy.open(is, gi.gridname);
315 if (! pj_gridinfo_load(is, gi))
317 //pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID );
326 /************************************************************************/
327 /* pj_apply_gridshift_3() */
329 /* This is the real workhorse, given a gridlist. */
330 /************************************************************************/
332 // Generic stream policy and standard grids
333 template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename Grids>
334 inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy,
337 std::vector<std::size_t> const& gridindexes,
340 typedef typename boost::range_size<Range>::type size_type;
342 // If the grids are empty the indexes are as well
343 if (gridindexes.empty())
345 //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
346 //return PJD_ERR_FAILED_TO_LOAD_GRID;
350 size_type point_count = boost::size(range);
352 for (size_type i = 0 ; i < point_count ; ++i)
354 typename boost::range_reference<Range>::type
355 point = range::at(range, i);
357 CalcT in_lon = geometry::get_as_radian<0>(point);
358 CalcT in_lat = geometry::get_as_radian<1>(point);
360 pj_gi * gip = find_grid(in_lon, in_lat, grids.gridinfo, gridindexes);
364 // load the grid shift info if we don't have it.
365 if (! gip->ct.cvs.empty() || load_grid(stream_policy, *gip))
367 // TODO: use set_invalid_point() or similar mechanism
368 CalcT out_lon = HUGE_VAL;
369 CalcT out_lat = HUGE_VAL;
371 nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip);
373 // TODO: check differently
374 if ( out_lon != HUGE_VAL )
376 geometry::set_from_radian<0>(point, out_lon);
377 geometry::set_from_radian<1>(point, out_lat);
386 // Generic stream policy and shared grids
387 template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename SharedGrids>
388 inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy,
391 std::vector<std::size_t> const& gridindexes,
394 typedef typename boost::range_size<Range>::type size_type;
396 // If the grids are empty the indexes are as well
397 if (gridindexes.empty())
399 //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
400 //return PJD_ERR_FAILED_TO_LOAD_GRID;
404 size_type point_count = boost::size(range);
409 for (size_type i = 0 ; i < point_count ; )
411 bool load_needed = false;
417 typename SharedGrids::read_locked lck_grids(grids);
419 for ( ; i < point_count ; ++i )
421 typename boost::range_reference<Range>::type
422 point = range::at(range, i);
424 in_lon = geometry::get_as_radian<0>(point);
425 in_lat = geometry::get_as_radian<1>(point);
427 pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes);
433 else if (! gip->ct.cvs.empty())
435 // TODO: use set_invalid_point() or similar mechanism
436 CalcT out_lon = HUGE_VAL;
437 CalcT out_lat = HUGE_VAL;
439 nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip);
441 // TODO: check differently
442 if (out_lon != HUGE_VAL)
444 geometry::set_from_radian<0>(point, out_lon);
445 geometry::set_from_radian<1>(point, out_lat);
460 if (load_grid(stream_policy, local_gi))
462 typename SharedGrids::write_locked lck_grids(grids);
464 // check again in case other thread already loaded the grid.
465 pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes);
467 if (gip != NULL && gip->ct.cvs.empty())
469 // swap loaded local storage with empty grid
484 /************************************************************************/
485 /* pj_apply_gridshift_2() */
487 /* This implementation uses the gridlist from a coordinate */
488 /* system definition. If the gridlist has not yet been */
489 /* populated in the coordinate system definition we set it up */
491 /************************************************************************/
493 template <bool Inverse, typename Par, typename Range, typename ProjGrids>
494 inline bool pj_apply_gridshift_2(Par const& /*defn*/, Range & range, ProjGrids const& proj_grids)
496 /*if( defn->catalog_name != NULL )
497 return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset,
500 /*std::vector<std::size_t> gridindexes;
501 pj_gridlist_from_nadgrids(pj_get_param_s(defn.params, "nadgrids"),
502 grids.storage_ptr->stream_policy,
503 grids.storage_ptr->grids,
506 // At this point the grids should be initialized
507 if (proj_grids.hindexes.empty())
510 return pj_apply_gridshift_3
512 Inverse, typename Par::type
513 >(proj_grids.grids_storage().stream_policy,
515 proj_grids.grids_storage().hgrids,
517 typename ProjGrids::grids_storage_type::grids_type::tag());
520 template <bool Inverse, typename Par, typename Range>
521 inline bool pj_apply_gridshift_2(Par const& , Range & , srs::detail::empty_projection_grids const& )
527 } // namespace detail
529 }}} // namespace boost::geometry::projections
531 #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP