123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- // Boost.Geometry
- // This file is manually converted from PROJ4
- // This file was modified by Oracle on 2018, 2019.
- // Modifications copyright (c) 2018-2019, Oracle and/or its affiliates.
- // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
- // Use, modification and distribution is subject to the Boost Software License,
- // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- // This file is converted from PROJ4, http://trac.osgeo.org/proj
- // PROJ4 is originally written by Gerald Evenden (then of the USGS)
- // PROJ4 is maintained by Frank Warmerdam
- // This file was converted to Geometry Library by Adam Wulkiewicz
- // Original copyright notice:
- // Author: Frank Warmerdam, warmerdam@pobox.com
- // Copyright (c) 2000, Frank Warmerdam
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the "Software"),
- // to deal in the Software without restriction, including without limitation
- // the rights to use, copy, modify, merge, publish, distribute, sublicense,
- // and/or sell copies of the Software, and to permit persons to whom the
- // Software is furnished to do so, subject to the following conditions:
- // The above copyright notice and this permission notice shall be included
- // in all copies or substantial portions of the Software.
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- // DEALINGS IN THE SOFTWARE.
- #ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP
- #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP
- #include <boost/geometry/core/assert.hpp>
- #include <boost/geometry/core/radian_access.hpp>
- #include <boost/geometry/srs/projections/impl/adjlon.hpp>
- #include <boost/geometry/srs/projections/impl/function_overloads.hpp>
- #include <boost/geometry/srs/projections/impl/pj_gridlist.hpp>
- #include <boost/geometry/util/range.hpp>
- namespace boost { namespace geometry { namespace projections
- {
- namespace detail
- {
- // Originally implemented in nad_intr.c
- template <typename CalcT>
- inline void nad_intr(CalcT in_lon, CalcT in_lat,
- CalcT & out_lon, CalcT & out_lat,
- pj_ctable const& ct)
- {
- pj_ctable::lp_t frct;
- pj_ctable::ilp_t indx;
- boost::int32_t in;
- indx.lam = int_floor(in_lon /= ct.del.lam);
- indx.phi = int_floor(in_lat /= ct.del.phi);
- frct.lam = in_lon - indx.lam;
- frct.phi = in_lat - indx.phi;
- // TODO: implement differently
- out_lon = out_lat = HUGE_VAL;
- if (indx.lam < 0) {
- if (indx.lam == -1 && frct.lam > 0.99999999999) {
- ++indx.lam;
- frct.lam = 0.;
- } else
- return;
- } else if ((in = indx.lam + 1) >= ct.lim.lam) {
- if (in == ct.lim.lam && frct.lam < 1e-11) {
- --indx.lam;
- frct.lam = 1.;
- } else
- return;
- }
- if (indx.phi < 0) {
- if (indx.phi == -1 && frct.phi > 0.99999999999) {
- ++indx.phi;
- frct.phi = 0.;
- } else
- return;
- } else if ((in = indx.phi + 1) >= ct.lim.phi) {
- if (in == ct.lim.phi && frct.phi < 1e-11) {
- --indx.phi;
- frct.phi = 1.;
- } else
- return;
- }
- boost::int32_t index = indx.phi * ct.lim.lam + indx.lam;
- pj_ctable::flp_t const& f00 = ct.cvs[index++];
- pj_ctable::flp_t const& f10 = ct.cvs[index];
- index += ct.lim.lam;
- pj_ctable::flp_t const& f11 = ct.cvs[index--];
- pj_ctable::flp_t const& f01 = ct.cvs[index];
- CalcT m00, m10, m01, m11;
- m11 = m10 = frct.lam;
- m00 = m01 = 1. - frct.lam;
- m11 *= frct.phi;
- m01 *= frct.phi;
- frct.phi = 1. - frct.phi;
- m00 *= frct.phi;
- m10 *= frct.phi;
- out_lon = m00 * f00.lam + m10 * f10.lam +
- m01 * f01.lam + m11 * f11.lam;
- out_lat = m00 * f00.phi + m10 * f10.phi +
- m01 * f01.phi + m11 * f11.phi;
- }
- // Originally implemented in nad_cvt.c
- template <bool Inverse, typename CalcT>
- inline void nad_cvt(CalcT const& in_lon, CalcT const& in_lat,
- CalcT & out_lon, CalcT & out_lat,
- pj_gi const& gi)
- {
- static const int max_iterations = 10;
- static const CalcT tol = 1e-12;
- static const CalcT toltol = tol * tol;
- static const CalcT pi = math::pi<CalcT>();
- // horizontal grid expected
- BOOST_GEOMETRY_ASSERT_MSG(gi.format != pj_gi::gtx,
- "Vertical grid cannot be used in horizontal shift.");
- pj_ctable const& ct = gi.ct;
- // TODO: implement differently
- if (in_lon == HUGE_VAL)
- {
- out_lon = HUGE_VAL;
- out_lat = HUGE_VAL;
- return;
- }
- // normalize input to ll origin
- pj_ctable::lp_t tb;
- tb.lam = in_lon - ct.ll.lam;
- tb.phi = in_lat - ct.ll.phi;
- tb.lam = adjlon (tb.lam - pi) + pi;
- pj_ctable::lp_t t;
- nad_intr(tb.lam, tb.phi, t.lam, t.phi, ct);
- if (t.lam == HUGE_VAL)
- {
- out_lon = HUGE_VAL;
- out_lat = HUGE_VAL;
- return;
- }
- if (! Inverse)
- {
- out_lon = in_lon - t.lam;
- out_lat = in_lat - t.phi;
- return;
- }
- t.lam = tb.lam + t.lam;
- t.phi = tb.phi - t.phi;
- int i = max_iterations;
- pj_ctable::lp_t del, dif;
- do
- {
- nad_intr(t.lam, t.phi, del.lam, del.phi, ct);
- // This case used to return failure, but I have
- // changed it to return the first order approximation
- // of the inverse shift. This avoids cases where the
- // grid shift *into* this grid came from another grid.
- // While we aren't returning optimally correct results
- // I feel a close result in this case is better than
- // no result. NFW
- // To demonstrate use -112.5839956 49.4914451 against
- // the NTv2 grid shift file from Canada.
- if (del.lam == HUGE_VAL)
- {
- // Inverse grid shift iteration failed, presumably at grid edge. Using first approximation.
- break;
- }
- dif.lam = t.lam - del.lam - tb.lam;
- dif.phi = t.phi + del.phi - tb.phi;
- t.lam -= dif.lam;
- t.phi -= dif.phi;
- }
- while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); // prob. slightly faster than hypot()
- if (i==0)
- {
- // Inverse grid shift iterator failed to converge.
- out_lon = HUGE_VAL;
- out_lat = HUGE_VAL;
- return;
- }
- out_lon = adjlon (t.lam + ct.ll.lam);
- out_lat = t.phi + ct.ll.phi;
- }
- /************************************************************************/
- /* find_grid() */
- /* */
- /* Determine which grid is the correct given an input coordinate. */
- /************************************************************************/
- // Originally find_ctable()
- // here divided into grid_disjoint(), find_grid() and load_grid()
- template <typename T>
- inline bool grid_disjoint(T const& lam, T const& phi,
- pj_ctable const& ct)
- {
- double epsilon = (fabs(ct.del.phi)+fabs(ct.del.lam))/10000.0;
- return ct.ll.phi - epsilon > phi
- || ct.ll.lam - epsilon > lam
- || (ct.ll.phi + (ct.lim.phi-1) * ct.del.phi + epsilon < phi)
- || (ct.ll.lam + (ct.lim.lam-1) * ct.del.lam + epsilon < lam);
- }
- template <typename T>
- inline pj_gi * find_grid(T const& lam,
- T const& phi,
- std::vector<pj_gi>::iterator first,
- std::vector<pj_gi>::iterator last)
- {
- pj_gi * gip = NULL;
- for( ; first != last ; ++first )
- {
- // skip tables that don't match our point at all.
- if (! grid_disjoint(lam, phi, first->ct))
- {
- // skip vertical grids
- if (first->format != pj_gi::gtx)
- {
- gip = boost::addressof(*first);
- break;
- }
- }
- }
- // If we didn't find a child then nothing more to do
- if( gip == NULL )
- return gip;
- // Otherwise use the child, first checking it's children
- pj_gi * child = find_grid(lam, phi, first->children.begin(), first->children.end());
- if (child != NULL)
- gip = child;
- return gip;
- }
- template <typename T>
- inline pj_gi * find_grid(T const& lam,
- T const& phi,
- pj_gridinfo & grids,
- std::vector<std::size_t> const& gridindexes)
- {
- pj_gi * gip = NULL;
- // keep trying till we find a table that works
- for (std::size_t i = 0 ; i < gridindexes.size() ; ++i)
- {
- pj_gi & gi = grids[gridindexes[i]];
- // skip tables that don't match our point at all.
- if (! grid_disjoint(lam, phi, gi.ct))
- {
- // skip vertical grids
- if (gi.format != pj_gi::gtx)
- {
- gip = boost::addressof(gi);
- break;
- }
- }
- }
- if (gip == NULL)
- return gip;
- // If we have child nodes, check to see if any of them apply.
- pj_gi * child = find_grid(lam, phi, gip->children.begin(), gip->children.end());
- if (child != NULL)
- gip = child;
- // if we get this far we have found a suitable grid
- return gip;
- }
- template <typename StreamPolicy>
- inline bool load_grid(StreamPolicy const& stream_policy, pj_gi_load & gi)
- {
- // load the grid shift info if we don't have it.
- if (gi.ct.cvs.empty())
- {
- typename StreamPolicy::stream_type is;
- stream_policy.open(is, gi.gridname);
- if (! pj_gridinfo_load(is, gi))
- {
- //pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID );
- return false;
- }
- }
- return true;
- }
- /************************************************************************/
- /* pj_apply_gridshift_3() */
- /* */
- /* This is the real workhorse, given a gridlist. */
- /************************************************************************/
- // Generic stream policy and standard grids
- template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename Grids>
- inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy,
- Range & range,
- Grids & grids,
- std::vector<std::size_t> const& gridindexes,
- grids_tag)
- {
- typedef typename boost::range_size<Range>::type size_type;
- // If the grids are empty the indexes are as well
- if (gridindexes.empty())
- {
- //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
- //return PJD_ERR_FAILED_TO_LOAD_GRID;
- return false;
- }
- size_type point_count = boost::size(range);
- for (size_type i = 0 ; i < point_count ; ++i)
- {
- typename boost::range_reference<Range>::type
- point = range::at(range, i);
- CalcT in_lon = geometry::get_as_radian<0>(point);
- CalcT in_lat = geometry::get_as_radian<1>(point);
- pj_gi * gip = find_grid(in_lon, in_lat, grids.gridinfo, gridindexes);
- if ( gip != NULL )
- {
- // load the grid shift info if we don't have it.
- if (! gip->ct.cvs.empty() || load_grid(stream_policy, *gip))
- {
- // TODO: use set_invalid_point() or similar mechanism
- CalcT out_lon = HUGE_VAL;
- CalcT out_lat = HUGE_VAL;
- nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip);
- // TODO: check differently
- if ( out_lon != HUGE_VAL )
- {
- geometry::set_from_radian<0>(point, out_lon);
- geometry::set_from_radian<1>(point, out_lat);
- }
- }
- }
- }
- return true;
- }
- // Generic stream policy and shared grids
- template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename SharedGrids>
- inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy,
- Range & range,
- SharedGrids & grids,
- std::vector<std::size_t> const& gridindexes,
- shared_grids_tag)
- {
- typedef typename boost::range_size<Range>::type size_type;
- // If the grids are empty the indexes are as well
- if (gridindexes.empty())
- {
- //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
- //return PJD_ERR_FAILED_TO_LOAD_GRID;
- return false;
- }
- size_type point_count = boost::size(range);
- // local storage
- pj_gi_load local_gi;
- for (size_type i = 0 ; i < point_count ; )
- {
- bool load_needed = false;
- CalcT in_lon = 0;
- CalcT in_lat = 0;
- {
- typename SharedGrids::read_locked lck_grids(grids);
- for ( ; i < point_count ; ++i )
- {
- typename boost::range_reference<Range>::type
- point = range::at(range, i);
- in_lon = geometry::get_as_radian<0>(point);
- in_lat = geometry::get_as_radian<1>(point);
- pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes);
- if (gip == NULL)
- {
- // do nothing
- }
- else if (! gip->ct.cvs.empty())
- {
- // TODO: use set_invalid_point() or similar mechanism
- CalcT out_lon = HUGE_VAL;
- CalcT out_lat = HUGE_VAL;
- nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip);
- // TODO: check differently
- if (out_lon != HUGE_VAL)
- {
- geometry::set_from_radian<0>(point, out_lon);
- geometry::set_from_radian<1>(point, out_lat);
- }
- }
- else
- {
- // loading is needed
- local_gi = *gip;
- load_needed = true;
- break;
- }
- }
- }
- if (load_needed)
- {
- if (load_grid(stream_policy, local_gi))
- {
- typename SharedGrids::write_locked lck_grids(grids);
- // check again in case other thread already loaded the grid.
- pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes);
- if (gip != NULL && gip->ct.cvs.empty())
- {
- // swap loaded local storage with empty grid
- local_gi.swap(*gip);
- }
- }
- else
- {
- ++i;
- }
- }
- }
- return true;
- }
- /************************************************************************/
- /* pj_apply_gridshift_2() */
- /* */
- /* This implementation uses the gridlist from a coordinate */
- /* system definition. If the gridlist has not yet been */
- /* populated in the coordinate system definition we set it up */
- /* now. */
- /************************************************************************/
- template <bool Inverse, typename Par, typename Range, typename ProjGrids>
- inline bool pj_apply_gridshift_2(Par const& /*defn*/, Range & range, ProjGrids const& proj_grids)
- {
- /*if( defn->catalog_name != NULL )
- return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset,
- x, y, z );*/
- /*std::vector<std::size_t> gridindexes;
- pj_gridlist_from_nadgrids(pj_get_param_s(defn.params, "nadgrids"),
- grids.storage_ptr->stream_policy,
- grids.storage_ptr->grids,
- gridindexes);*/
- // At this point the grids should be initialized
- if (proj_grids.hindexes.empty())
- return false;
- return pj_apply_gridshift_3
- <
- Inverse, typename Par::type
- >(proj_grids.grids_storage().stream_policy,
- range,
- proj_grids.grids_storage().hgrids,
- proj_grids.hindexes,
- typename ProjGrids::grids_storage_type::grids_type::tag());
- }
- template <bool Inverse, typename Par, typename Range>
- inline bool pj_apply_gridshift_2(Par const& , Range & , srs::detail::empty_projection_grids const& )
- {
- return false;
- }
- } // namespace detail
- }}} // namespace boost::geometry::projections
- #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP
|