1 // Boost.Geometry - gis-projections (based on PROJ4)
3 // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2017, 2018, 2019.
6 // Modifications copyright (c) 2017-2019, Oracle and/or its affiliates.
7 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.
9 // Use, modification and distribution is subject to the Boost Software License,
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
13 // This file is converted from PROJ4, http://trac.osgeo.org/proj
14 // PROJ4 is originally written by Gerald Evenden (then of the USGS)
15 // PROJ4 is maintained by Frank Warmerdam
16 // PROJ4 is converted to Boost.Geometry by Barend Gehrels
18 // Last updated version of proj: 5.0.0
20 // Original copyright notice:
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_PROJECTIONS_OB_TRAN_HPP
41 #define BOOST_GEOMETRY_PROJECTIONS_OB_TRAN_HPP
43 #include <boost/geometry/util/math.hpp>
44 #include <boost/shared_ptr.hpp>
46 #include <boost/geometry/srs/projections/impl/aasincos.hpp>
47 #include <boost/geometry/srs/projections/impl/base_static.hpp>
48 #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
49 #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
50 #include <boost/geometry/srs/projections/impl/pj_ell_set.hpp>
51 #include <boost/geometry/srs/projections/impl/projects.hpp>
53 namespace boost { namespace geometry
58 #ifndef DOXYGEN_NO_DETAIL
61 // fwd declaration needed below
63 inline detail::dynamic_wrapper_b<T, projections::parameters<T> >*
64 create_new(srs::detail::proj4_parameters const& params,
65 projections::parameters<T> const& parameters);
68 inline detail::dynamic_wrapper_b<T, projections::parameters<T> >*
69 create_new(srs::dpar::parameters<T> const& params,
70 projections::parameters<T> const& parameters);
74 namespace detail { namespace ob_tran
77 static const double tolerance = 1e-10;
79 template <typename Parameters>
80 inline Parameters o_proj_parameters(srs::detail::proj4_parameters const& params,
81 Parameters const& par)
83 /* copy existing header into new */
86 /* get name of projection to be translated */
87 pj.id = pj_get_param_s(params, "o_proj");
88 if (pj.id.is_unknown())
89 BOOST_THROW_EXCEPTION( projection_exception(error_no_rotation_proj) );
91 /* avoid endless recursion */
92 if( pj.id.name == "ob_tran")
93 BOOST_THROW_EXCEPTION( projection_exception(error_failed_to_find_proj) );
95 // Commented out for consistency with Proj4 >= 5.0.0
96 /* force spherical earth */
97 //pj.one_es = pj.rone_es = 1.;
103 template <typename T, typename Parameters>
104 inline Parameters o_proj_parameters(srs::dpar::parameters<T> const& params,
105 Parameters const& par)
107 /* copy existing header into new */
110 /* get name of projection to be translated */
111 typename srs::dpar::parameters<T>::const_iterator
112 it = pj_param_find(params, srs::dpar::o_proj);
113 if (it != params.end())
114 pj.id = static_cast<srs::dpar::value_proj>(it->template get_value<int>());
116 BOOST_THROW_EXCEPTION( projection_exception(error_no_rotation_proj) );
118 /* avoid endless recursion */
119 if( pj.id.id == srs::dpar::proj_ob_tran)
120 BOOST_THROW_EXCEPTION( projection_exception(error_failed_to_find_proj) );
122 // Commented out for consistency with Proj4 >= 5.0.0
123 /* force spherical earth */
124 //pj.one_es = pj.rone_es = 1.;
130 template <BOOST_GEOMETRY_PROJECTIONS_DETAIL_TYPENAME_PX, typename Parameters>
131 inline Parameters o_proj_parameters(srs::spar::parameters<BOOST_GEOMETRY_PROJECTIONS_DETAIL_PX> const& /*params*/,
132 Parameters const& par)
134 /* copy existing header into new */
137 /* get name of projection to be translated */
138 typedef srs::spar::parameters<BOOST_GEOMETRY_PROJECTIONS_DETAIL_PX> params_type;
139 typedef typename srs::spar::detail::tuples_find_if
142 srs::spar::detail::is_param_t<srs::spar::o_proj>::pred
145 static const bool is_found = srs::spar::detail::tuples_is_found<o_proj_type>::value;
146 BOOST_MPL_ASSERT_MSG((is_found), NO_ROTATION_PROJ, (params_type));
148 typedef typename o_proj_type::type proj_type;
149 static const bool is_specialized = srs::spar::detail::proj_traits<proj_type>::is_specialized;
150 BOOST_MPL_ASSERT_MSG((is_specialized), NO_ROTATION_PROJ, (params_type));
152 pj.id = srs::spar::detail::proj_traits<proj_type>::id;
154 /* avoid endless recursion */
155 static const bool is_non_resursive = ! boost::is_same<proj_type, srs::spar::proj_ob_tran>::value;
156 BOOST_MPL_ASSERT_MSG((is_non_resursive), INVALID_O_PROJ_PARAMETER, (params_type));
158 // Commented out for consistency with Proj4 >= 5.0.0
159 /* force spherical earth */
160 //pj.one_es = pj.rone_es = 1.;
166 // TODO: It's possible that the original Parameters could be used
167 // instead of a copy in link.
168 // But it's not possible with the current implementation of
169 // dynamic_wrapper_b always storing params
171 template <typename T, typename Parameters>
174 template <typename Params>
175 par_ob_tran(Params const& params, Parameters const& par)
176 : link(projections::detail::create_new(params, o_proj_parameters(params, par)))
179 BOOST_THROW_EXCEPTION( projection_exception(error_unknown_projection_id) );
182 inline void fwd(T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
184 link->fwd(link->params(), lp_lon, lp_lat, xy_x, xy_y);
187 inline void inv(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
189 link->inv(link->params(), xy_x, xy_y, lp_lon, lp_lat);
192 boost::shared_ptr<dynamic_wrapper_b<T, Parameters> > link;
197 template <typename StaticParameters, typename T, typename Parameters>
198 struct par_ob_tran_static
200 // this metafunction handles static error handling
201 typedef typename srs::spar::detail::pick_o_proj_tag
206 /* avoid endless recursion */
207 static const bool is_o_proj_not_ob_tran = ! boost::is_same<o_proj_tag, srs::spar::proj_ob_tran>::value;
208 BOOST_MPL_ASSERT_MSG((is_o_proj_not_ob_tran), INVALID_O_PROJ_PARAMETER, (StaticParameters));
210 typedef typename projections::detail::static_projection_type
213 // Commented out for consistency with Proj4 >= 5.0.0
214 //srs_sphere_tag, // force spherical
215 typename projections::detail::static_srs_tag<StaticParameters>::type,
219 >::type projection_type;
221 par_ob_tran_static(StaticParameters const& params, Parameters const& par)
222 : link(params, o_proj_parameters(params, par))
225 inline void fwd(T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
227 link.fwd(link.params(), lp_lon, lp_lat, xy_x, xy_y);
230 inline void inv(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
232 link.inv(link.params(), xy_x, xy_y, lp_lon, lp_lat);
235 projection_type link;
240 template <typename T, typename Par>
241 inline void o_forward(T lp_lon, T lp_lat, T& xy_x, T& xy_y, Par const& proj_parm)
243 T coslam, sinphi, cosphi;
245 coslam = cos(lp_lon);
246 sinphi = sin(lp_lat);
247 cosphi = cos(lp_lat);
248 lp_lon = adjlon(aatan2(cosphi * sin(lp_lon), proj_parm.sphip * cosphi * coslam +
249 proj_parm.cphip * sinphi) + proj_parm.lamp);
250 lp_lat = aasin(proj_parm.sphip * sinphi - proj_parm.cphip * cosphi * coslam);
252 proj_parm.fwd(lp_lon, lp_lat, xy_x, xy_y);
255 template <typename T, typename Par>
256 inline void o_inverse(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat, Par const& proj_parm)
258 T coslam, sinphi, cosphi;
260 proj_parm.inv(xy_x, xy_y, lp_lon, lp_lat);
261 if (lp_lon != HUGE_VAL) {
262 coslam = cos(lp_lon -= proj_parm.lamp);
263 sinphi = sin(lp_lat);
264 cosphi = cos(lp_lat);
265 lp_lat = aasin(proj_parm.sphip * sinphi + proj_parm.cphip * cosphi * coslam);
266 lp_lon = aatan2(cosphi * sin(lp_lon), proj_parm.sphip * cosphi * coslam -
267 proj_parm.cphip * sinphi);
271 template <typename T, typename Par>
272 inline void t_forward(T lp_lon, T lp_lat, T& xy_x, T& xy_y, Par const& proj_parm)
276 cosphi = cos(lp_lat);
277 coslam = cos(lp_lon);
278 lp_lon = adjlon(aatan2(cosphi * sin(lp_lon), sin(lp_lat)) + proj_parm.lamp);
279 lp_lat = aasin(- cosphi * coslam);
281 proj_parm.fwd(lp_lon, lp_lat, xy_x, xy_y);
284 template <typename T, typename Par>
285 inline void t_inverse(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat, Par const& proj_parm)
289 proj_parm.inv(xy_x, xy_y, lp_lon, lp_lat);
290 if (lp_lon != HUGE_VAL) {
291 cosphi = cos(lp_lat);
292 t = lp_lon - proj_parm.lamp;
293 lp_lon = aatan2(cosphi * sin(t), - sin(lp_lat));
294 lp_lat = aasin(cosphi * cos(t));
298 // General Oblique Transformation
299 template <typename T, typename Params, typename Parameters, typename ProjParameters>
300 inline T setup_ob_tran(Params const& params, Parameters & /*par*/, ProjParameters& proj_parm)
302 static const T half_pi = detail::half_pi<T>();
306 // Commented out for consistency with Proj4 >= 5.0.0
307 //par.es = 0.; /* force to spherical */
309 // proj_parm.link should be created at this point
311 if (pj_param_r<srs::spar::o_alpha>(params, "o_alpha", srs::dpar::o_alpha, alpha)) {
314 lamc = pj_get_param_r<T, srs::spar::o_lon_c>(params, "o_lon_c", srs::dpar::o_lon_c);
315 phic = pj_get_param_r<T, srs::spar::o_lon_c>(params, "o_lat_c", srs::dpar::o_lat_c);
316 //alpha = pj_get_param_r(par.params, "o_alpha");
318 if (fabs(fabs(phic) - half_pi) <= tolerance)
319 BOOST_THROW_EXCEPTION( projection_exception(error_lat_0_or_alpha_eq_90) );
321 proj_parm.lamp = lamc + aatan2(-cos(alpha), -sin(alpha) * sin(phic));
322 phip = aasin(cos(phic) * sin(alpha));
323 } else if (pj_param_r<srs::spar::o_lat_p>(params, "o_lat_p", srs::dpar::o_lat_p, phip)) { /* specified new pole */
324 proj_parm.lamp = pj_get_param_r<T, srs::spar::o_lon_p>(params, "o_lon_p", srs::dpar::o_lon_p);
325 //phip = pj_param_r(par.params, "o_lat_p");
326 } else { /* specified new "equator" points */
327 T lam1, lam2, phi1, phi2, con;
329 lam1 = pj_get_param_r<T, srs::spar::o_lon_1>(params, "o_lon_1", srs::dpar::o_lon_1);
330 phi1 = pj_get_param_r<T, srs::spar::o_lat_1>(params, "o_lat_1", srs::dpar::o_lat_1);
331 lam2 = pj_get_param_r<T, srs::spar::o_lon_2>(params, "o_lon_2", srs::dpar::o_lon_2);
332 phi2 = pj_get_param_r<T, srs::spar::o_lat_2>(params, "o_lat_2", srs::dpar::o_lat_2);
333 if (fabs(phi1 - phi2) <= tolerance || (con = fabs(phi1)) <= tolerance ||
334 fabs(con - half_pi) <= tolerance || fabs(fabs(phi2) - half_pi) <= tolerance)
335 BOOST_THROW_EXCEPTION( projection_exception(error_lat_1_or_2_zero_or_90) );
337 proj_parm.lamp = atan2(cos(phi1) * sin(phi2) * cos(lam1) -
338 sin(phi1) * cos(phi2) * cos(lam2),
339 sin(phi1) * cos(phi2) * sin(lam2) -
340 cos(phi1) * sin(phi2) * sin(lam1));
341 phip = atan(-cos(proj_parm.lamp - lam1) / tan(phi1));
344 if (fabs(phip) > tolerance) { /* oblique */
345 proj_parm.cphip = cos(phip);
346 proj_parm.sphip = sin(phip);
347 } else { /* transverse */
351 /* Support some rather speculative test cases, where the rotated projection */
352 /* is actually latlong. We do not want scaling in that case... */
353 //if (proj_parm.link...mutable_parameters().right==PJ_IO_UNITS_ANGULAR)
354 // par.right = PJ_IO_UNITS_PROJECTED;
356 // return phip to choose model
360 template <typename T, typename Parameters>
361 struct base_ob_tran_oblique
363 par_ob_tran<T, Parameters> m_proj_parm;
365 inline base_ob_tran_oblique(par_ob_tran<T, Parameters> const& proj_parm)
366 : m_proj_parm(proj_parm)
369 // FORWARD(o_forward) spheroid
370 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
371 inline void fwd(Parameters const& , T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
373 // NOTE: Parameters ignored, m_proj_parm.link has a copy
374 o_forward(lp_lon, lp_lat, xy_x, xy_y, this->m_proj_parm);
377 // INVERSE(o_inverse) spheroid
378 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
379 inline void inv(Parameters const& , T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
381 // NOTE: Parameters ignored, m_proj_parm.link has a copy
382 o_inverse(xy_x, xy_y, lp_lon, lp_lat, this->m_proj_parm);
385 static inline std::string get_name()
387 return "ob_tran_oblique";
392 template <typename T, typename Parameters>
393 struct base_ob_tran_transverse
395 par_ob_tran<T, Parameters> m_proj_parm;
397 inline base_ob_tran_transverse(par_ob_tran<T, Parameters> const& proj_parm)
398 : m_proj_parm(proj_parm)
401 // FORWARD(t_forward) spheroid
402 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
403 inline void fwd(Parameters const& , T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
405 // NOTE: Parameters ignored, m_proj_parm.link has a copy
406 t_forward(lp_lon, lp_lat, xy_x, xy_y, this->m_proj_parm);
409 // INVERSE(t_inverse) spheroid
410 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
411 inline void inv(Parameters const& , T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
413 // NOTE: Parameters ignored, m_proj_parm.link has a copy
414 t_inverse(xy_x, xy_y, lp_lon, lp_lat, this->m_proj_parm);
417 static inline std::string get_name()
419 return "ob_tran_transverse";
424 template <typename StaticParameters, typename T, typename Parameters>
425 struct base_ob_tran_static
427 par_ob_tran_static<StaticParameters, T, Parameters> m_proj_parm;
430 inline base_ob_tran_static(StaticParameters const& params, Parameters const& par)
431 : m_proj_parm(params, par)
434 // FORWARD(o_forward) spheroid
435 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
436 inline void fwd(Parameters const& , T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
438 // NOTE: Parameters ignored, m_proj_parm.link has a copy
440 o_forward(lp_lon, lp_lat, xy_x, xy_y, this->m_proj_parm);
442 t_forward(lp_lon, lp_lat, xy_x, xy_y, this->m_proj_parm);
446 // INVERSE(o_inverse) spheroid
447 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
448 inline void inv(Parameters const& , T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
450 // NOTE: Parameters ignored, m_proj_parm.link has a copy
452 o_inverse(xy_x, xy_y, lp_lon, lp_lat, this->m_proj_parm);
454 t_inverse(xy_x, xy_y, lp_lon, lp_lat, this->m_proj_parm);
458 static inline std::string get_name()
465 }} // namespace detail::ob_tran
469 \brief General Oblique Transformation projection
471 \tparam Geographic latlong point type
472 \tparam Cartesian xy point type
473 \tparam Parameters parameter type
474 \par Projection characteristics
477 \par Projection parameters
479 - Plus projection parameters
483 - o_alpha: Alpha (degrees)
487 - o_lat_1: Latitude of first standard parallel (degrees)
489 - o_lat_2: Latitude of second standard parallel (degrees)
491 \image html ex_ob_tran.gif
493 template <typename T, typename Parameters>
494 struct ob_tran_oblique : public detail::ob_tran::base_ob_tran_oblique<T, Parameters>
496 template <typename Params>
497 inline ob_tran_oblique(Params const& , Parameters const& ,
498 detail::ob_tran::par_ob_tran<T, Parameters> const& proj_parm)
499 : detail::ob_tran::base_ob_tran_oblique<T, Parameters>(proj_parm)
502 //detail::ob_tran::setup_ob_tran(this->m_par, this->m_proj_parm);
507 \brief General Oblique Transformation projection
509 \tparam Geographic latlong point type
510 \tparam Cartesian xy point type
511 \tparam Parameters parameter type
512 \par Projection characteristics
515 \par Projection parameters
517 - Plus projection parameters
521 - o_alpha: Alpha (degrees)
525 - o_lat_1: Latitude of first standard parallel (degrees)
527 - o_lat_2: Latitude of second standard parallel (degrees)
529 \image html ex_ob_tran.gif
531 template <typename T, typename Parameters>
532 struct ob_tran_transverse : public detail::ob_tran::base_ob_tran_transverse<T, Parameters>
534 template <typename Params>
535 inline ob_tran_transverse(Params const& , Parameters const& ,
536 detail::ob_tran::par_ob_tran<T, Parameters> const& proj_parm)
537 : detail::ob_tran::base_ob_tran_transverse<T, Parameters>(proj_parm)
540 //detail::ob_tran::setup_ob_tran(this->m_par, this->m_proj_parm);
545 \brief General Oblique Transformation projection
547 \tparam Geographic latlong point type
548 \tparam Cartesian xy point type
549 \tparam Parameters parameter type
550 \par Projection characteristics
553 \par Projection parameters
555 - Plus projection parameters
559 - o_alpha: Alpha (degrees)
563 - o_lat_1: Latitude of first standard parallel (degrees)
565 - o_lat_2: Latitude of second standard parallel (degrees)
567 \image html ex_ob_tran.gif
569 template <typename StaticParameters, typename T, typename Parameters>
570 struct ob_tran_static : public detail::ob_tran::base_ob_tran_static<StaticParameters, T, Parameters>
572 inline ob_tran_static(StaticParameters const& params, Parameters const& par)
573 : detail::ob_tran::base_ob_tran_static<StaticParameters, T, Parameters>(params, par)
575 T phip = detail::ob_tran::setup_ob_tran<T>(params, par, this->m_proj_parm);
576 this->m_is_oblique = fabs(phip) > detail::ob_tran::tolerance;
580 #ifndef DOXYGEN_NO_DETAIL
585 template <typename SP, typename CT, typename P>
586 struct static_projection_type<srs::spar::proj_ob_tran, srs_sphere_tag, SP, CT, P>
588 typedef static_wrapper_fi<ob_tran_static<SP, CT, P>, P> type;
590 template <typename SP, typename CT, typename P>
591 struct static_projection_type<srs::spar::proj_ob_tran, srs_spheroid_tag, SP, CT, P>
593 typedef static_wrapper_fi<ob_tran_static<SP, CT, P>, P> type;
597 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_BEGIN(ob_tran_entry)
599 Parameters parameters_copy = parameters;
600 detail::ob_tran::par_ob_tran<T, Parameters> proj_parm(params, parameters_copy);
601 T phip = detail::ob_tran::setup_ob_tran<T>(params, parameters_copy, proj_parm);
603 if (fabs(phip) > detail::ob_tran::tolerance)
604 return new dynamic_wrapper_fi<ob_tran_oblique<T, Parameters>, T, Parameters>(params, parameters_copy, proj_parm);
606 return new dynamic_wrapper_fi<ob_tran_transverse<T, Parameters>, T, Parameters>(params, parameters_copy, proj_parm);
608 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_END
610 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(ob_tran_init)
612 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(ob_tran, ob_tran_entry)
615 } // namespace detail
618 } // namespace projections
620 }} // namespace boost::geometry
622 #endif // BOOST_GEOMETRY_PROJECTIONS_OB_TRAN_HPP