etmerc.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Boost.Geometry - gis-projections (based on PROJ4)
  2. // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2017, 2018.
  4. // Modifications copyright (c) 2017-2018, 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. // PROJ4 is converted to Boost.Geometry by Barend Gehrels
  13. // Last updated version of proj: 5.0.0
  14. // Original copyright notice:
  15. // Copyright (c) 2008 Gerald I. Evenden
  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. /* The code in this file is largly based upon procedures:
  32. *
  33. * Written by: Knud Poder and Karsten Engsager
  34. *
  35. * Based on math from: R.Koenig and K.H. Weise, "Mathematische
  36. * Grundlagen der hoeheren Geodaesie und Kartographie,
  37. * Springer-Verlag, Berlin/Goettingen" Heidelberg, 1951.
  38. *
  39. * Modified and used here by permission of Reference Networks
  40. * Division, Kort og Matrikelstyrelsen (KMS), Copenhagen, Denmark
  41. */
  42. #ifndef BOOST_GEOMETRY_PROJECTIONS_ETMERC_HPP
  43. #define BOOST_GEOMETRY_PROJECTIONS_ETMERC_HPP
  44. #include <boost/math/special_functions/hypot.hpp>
  45. #include <boost/geometry/srs/projections/impl/base_static.hpp>
  46. #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
  47. #include <boost/geometry/srs/projections/impl/projects.hpp>
  48. #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
  49. #include <boost/geometry/srs/projections/impl/function_overloads.hpp>
  50. namespace boost { namespace geometry
  51. {
  52. namespace srs { namespace par4
  53. {
  54. struct etmerc {};
  55. struct utm {};
  56. }} //namespace srs::par4
  57. namespace projections
  58. {
  59. #ifndef DOXYGEN_NO_DETAIL
  60. namespace detail { namespace etmerc
  61. {
  62. static const int PROJ_ETMERC_ORDER = 6;
  63. template <typename T>
  64. struct par_etmerc
  65. {
  66. T Qn; /* Merid. quad., scaled to the projection */
  67. T Zb; /* Radius vector in polar coord. systems */
  68. T cgb[6]; /* Constants for Gauss -> Geo lat */
  69. T cbg[6]; /* Constants for Geo lat -> Gauss */
  70. T utg[6]; /* Constants for transv. merc. -> geo */
  71. T gtu[6]; /* Constants for geo -> transv. merc. */
  72. };
  73. template <typename T>
  74. inline T log1py(T const& x) { /* Compute log(1+x) accurately */
  75. volatile T
  76. y = 1 + x,
  77. z = y - 1;
  78. /* Here's the explanation for this magic: y = 1 + z, exactly, and z
  79. * approx x, thus log(y)/z (which is nearly constant near z = 0) returns
  80. * a good approximation to the true log(1 + x)/x. The multiplication x *
  81. * (log(y)/z) introduces little additional error. */
  82. return z == 0 ? x : x * log(y) / z;
  83. }
  84. template <typename T>
  85. inline T asinhy(T const& x) { /* Compute asinh(x) accurately */
  86. T y = fabs(x); /* Enforce odd parity */
  87. y = log1py(y * (1 + y/(boost::math::hypot(1.0, y) + 1)));
  88. return x < 0 ? -y : y;
  89. }
  90. template <typename T>
  91. inline T gatg(const T *p1, int len_p1, T const& B) {
  92. const T *p;
  93. T h = 0, h1, h2 = 0, cos_2B;
  94. cos_2B = 2*cos(2*B);
  95. for (p = p1 + len_p1, h1 = *--p; p - p1; h2 = h1, h1 = h)
  96. h = -h2 + cos_2B*h1 + *--p;
  97. return (B + h*sin(2*B));
  98. }
  99. /* Complex Clenshaw summation */
  100. template <typename T>
  101. inline T clenS(const T *a, int size, T const& arg_r, T const& arg_i, T *R, T *I) {
  102. T r, i, hr, hr1, hr2, hi, hi1, hi2;
  103. T sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i;
  104. /* arguments */
  105. const T* p = a + size;
  106. sin_arg_r = sin(arg_r);
  107. cos_arg_r = cos(arg_r);
  108. sinh_arg_i = sinh(arg_i);
  109. cosh_arg_i = cosh(arg_i);
  110. r = 2*cos_arg_r*cosh_arg_i;
  111. i = -2*sin_arg_r*sinh_arg_i;
  112. /* summation loop */
  113. for (hi1 = hr1 = hi = 0, hr = *--p; a - p;) {
  114. hr2 = hr1;
  115. hi2 = hi1;
  116. hr1 = hr;
  117. hi1 = hi;
  118. hr = -hr2 + r*hr1 - i*hi1 + *--p;
  119. hi = -hi2 + i*hr1 + r*hi1;
  120. }
  121. r = sin_arg_r*cosh_arg_i;
  122. i = cos_arg_r*sinh_arg_i;
  123. *R = r*hr - i*hi;
  124. *I = r*hi + i*hr;
  125. return(*R);
  126. }
  127. /* Real Clenshaw summation */
  128. template <typename T>
  129. inline T clens(const T *a, int size, T const& arg_r) {
  130. T r, hr, hr1, hr2, cos_arg_r;
  131. const T* p = a + size;
  132. cos_arg_r = cos(arg_r);
  133. r = 2*cos_arg_r;
  134. /* summation loop */
  135. for (hr1 = 0, hr = *--p; a - p;) {
  136. hr2 = hr1;
  137. hr1 = hr;
  138. hr = -hr2 + r*hr1 + *--p;
  139. }
  140. return(sin(arg_r)*hr);
  141. }
  142. // template class, using CRTP to implement forward/inverse
  143. template <typename T, typename Parameters>
  144. struct base_etmerc_ellipsoid
  145. : public base_t_fi<base_etmerc_ellipsoid<T, Parameters>, T, Parameters>
  146. {
  147. par_etmerc<T> m_proj_parm;
  148. inline base_etmerc_ellipsoid(const Parameters& par)
  149. : base_t_fi<base_etmerc_ellipsoid<T, Parameters>, T, Parameters>(*this, par)
  150. {}
  151. // FORWARD(e_forward) ellipsoid
  152. // Project coordinates from geographic (lon, lat) to cartesian (x, y)
  153. inline void fwd(T& lp_lon, T& lp_lat, T& xy_x, T& xy_y) const
  154. {
  155. T sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe;
  156. T Cn = lp_lat, Ce = lp_lon;
  157. /* ell. LAT, LNG -> Gaussian LAT, LNG */
  158. Cn = gatg(this->m_proj_parm.cbg, PROJ_ETMERC_ORDER, Cn);
  159. /* Gaussian LAT, LNG -> compl. sph. LAT */
  160. sin_Cn = sin(Cn);
  161. cos_Cn = cos(Cn);
  162. sin_Ce = sin(Ce);
  163. cos_Ce = cos(Ce);
  164. Cn = atan2(sin_Cn, cos_Ce*cos_Cn);
  165. Ce = atan2(sin_Ce*cos_Cn, boost::math::hypot(sin_Cn, cos_Cn*cos_Ce));
  166. /* compl. sph. N, E -> ell. norm. N, E */
  167. Ce = asinhy(tan(Ce)); /* Replaces: Ce = log(tan(fourth_pi + Ce*0.5)); */
  168. Cn += clenS(this->m_proj_parm.gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe);
  169. Ce += dCe;
  170. if (fabs(Ce) <= 2.623395162778) {
  171. xy_y = this->m_proj_parm.Qn * Cn + this->m_proj_parm.Zb; /* Northing */
  172. xy_x = this->m_proj_parm.Qn * Ce; /* Easting */
  173. } else
  174. xy_x = xy_y = HUGE_VAL;
  175. }
  176. // INVERSE(e_inverse) ellipsoid
  177. // Project coordinates from cartesian (x, y) to geographic (lon, lat)
  178. inline void inv(T& xy_x, T& xy_y, T& lp_lon, T& lp_lat) const
  179. {
  180. T sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe;
  181. T Cn = xy_y, Ce = xy_x;
  182. /* normalize N, E */
  183. Cn = (Cn - this->m_proj_parm.Zb)/this->m_proj_parm.Qn;
  184. Ce = Ce/this->m_proj_parm.Qn;
  185. if (fabs(Ce) <= 2.623395162778) { /* 150 degrees */
  186. /* norm. N, E -> compl. sph. LAT, LNG */
  187. Cn += clenS(this->m_proj_parm.utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe);
  188. Ce += dCe;
  189. Ce = atan(sinh(Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - fourth_pi); */
  190. /* compl. sph. LAT -> Gaussian LAT, LNG */
  191. sin_Cn = sin(Cn);
  192. cos_Cn = cos(Cn);
  193. sin_Ce = sin(Ce);
  194. cos_Ce = cos(Ce);
  195. Ce = atan2(sin_Ce, cos_Ce*cos_Cn);
  196. Cn = atan2(sin_Cn*cos_Ce, boost::math::hypot(sin_Ce, cos_Ce*cos_Cn));
  197. /* Gaussian LAT, LNG -> ell. LAT, LNG */
  198. lp_lat = gatg(this->m_proj_parm.cgb, PROJ_ETMERC_ORDER, Cn);
  199. lp_lon = Ce;
  200. }
  201. else
  202. lp_lat = lp_lon = HUGE_VAL;
  203. }
  204. static inline std::string get_name()
  205. {
  206. return "etmerc_ellipsoid";
  207. }
  208. };
  209. template <typename Parameters, typename T>
  210. inline void setup(Parameters& par, par_etmerc<T>& proj_parm)
  211. {
  212. T f, n, np, Z;
  213. if (par.es <= 0) {
  214. BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
  215. }
  216. f = par.es / (1 + sqrt(1 - par.es)); /* Replaces: f = 1 - sqrt(1-par.es); */
  217. /* third flattening */
  218. np = n = f/(2 - f);
  219. /* COEF. OF TRIG SERIES GEO <-> GAUSS */
  220. /* cgb := Gaussian -> Geodetic, KW p190 - 191 (61) - (62) */
  221. /* cbg := Geodetic -> Gaussian, KW p186 - 187 (51) - (52) */
  222. /* PROJ_ETMERC_ORDER = 6th degree : Engsager and Poder: ICC2007 */
  223. proj_parm.cgb[0] = n*( 2 + n*(-2/3.0 + n*(-2 + n*(116/45.0 + n*(26/45.0 +
  224. n*(-2854/675.0 ))))));
  225. proj_parm.cbg[0] = n*(-2 + n*( 2/3.0 + n*( 4/3.0 + n*(-82/45.0 + n*(32/45.0 +
  226. n*( 4642/4725.0))))));
  227. np *= n;
  228. proj_parm.cgb[1] = np*(7/3.0 + n*( -8/5.0 + n*(-227/45.0 + n*(2704/315.0 +
  229. n*( 2323/945.0)))));
  230. proj_parm.cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0 + n*( 904/315.0 +
  231. n*(-1522/945.0)))));
  232. np *= n;
  233. /* n^5 coeff corrected from 1262/105 -> -1262/105 */
  234. proj_parm.cgb[2] = np*( 56/15.0 + n*(-136/35.0 + n*(-1262/105.0 +
  235. n*( 73814/2835.0))));
  236. proj_parm.cbg[2] = np*(-26/15.0 + n*( 34/21.0 + n*( 8/5.0 +
  237. n*(-12686/2835.0))));
  238. np *= n;
  239. /* n^5 coeff corrected from 322/35 -> 332/35 */
  240. proj_parm.cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0)));
  241. proj_parm.cbg[3] = np*(1237/630.0 + n*( -12/5.0 + n*( -24832/14175.0)));
  242. np *= n;
  243. proj_parm.cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 ));
  244. proj_parm.cbg[4] = np*(-734/315.0 + n*( 109598/31185.0));
  245. np *= n;
  246. proj_parm.cgb[5] = np*(601676/22275.0 );
  247. proj_parm.cbg[5] = np*(444337/155925.0);
  248. /* Constants of the projections */
  249. /* Transverse Mercator (UTM, ITM, etc) */
  250. np = n*n;
  251. /* Norm. mer. quad, K&W p.50 (96), p.19 (38b), p.5 (2) */
  252. proj_parm.Qn = par.k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0)));
  253. /* coef of trig series */
  254. /* utg := ell. N, E -> sph. N, E, KW p194 (65) */
  255. /* gtu := sph. N, E -> ell. N, E, KW p196 (69) */
  256. proj_parm.utg[0] = n*(-0.5 + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 +
  257. n*( 81/512.0 + n*(-96199/604800.0))))));
  258. proj_parm.gtu[0] = n*( 0.5 + n*(-2/3.0 + n*( 5/16.0 + n*(41/180.0 +
  259. n*(-127/288.0 + n*( 7891/37800.0 ))))));
  260. proj_parm.utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 +
  261. n*( 1118711/3870720.0)))));
  262. proj_parm.gtu[1] = np*(13/48.0 + n*(-3/5.0 + n*(557/1440.0 + n*(281/630.0 +
  263. n*(-1983433/1935360.0)))));
  264. np *= n;
  265. proj_parm.utg[2] = np*(-17/480.0 + n*( 37/840.0 + n*( 209/4480.0 +
  266. n*( -5569/90720.0 ))));
  267. proj_parm.gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 +
  268. n*(167603/181440.0))));
  269. np *= n;
  270. proj_parm.utg[3] = np*(-4397/161280.0 + n*( 11/504.0 + n*( 830251/7257600.0)));
  271. proj_parm.gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0)));
  272. np *= n;
  273. proj_parm.utg[4] = np*(-4583/161280.0 + n*( 108847/3991680.0));
  274. proj_parm.gtu[4] = np*(34729/80640.0 + n*(-3418889/1995840.0));
  275. np *= n;
  276. proj_parm.utg[5] = np*(-20648693/638668800.0);
  277. proj_parm.gtu[5] = np*(212378941/319334400.0);
  278. /* Gaussian latitude value of the origin latitude */
  279. Z = gatg(proj_parm.cbg, PROJ_ETMERC_ORDER, par.phi0);
  280. /* Origin northing minus true northing at the origin latitude */
  281. /* i.e. true northing = N - proj_parm.Zb */
  282. proj_parm.Zb = - proj_parm.Qn*(Z + clens(proj_parm.gtu, PROJ_ETMERC_ORDER, 2*Z));
  283. }
  284. // Extended Transverse Mercator
  285. template <typename Parameters, typename T>
  286. inline void setup_etmerc(Parameters& par, par_etmerc<T>& proj_parm)
  287. {
  288. setup(par, proj_parm);
  289. }
  290. // Universal Transverse Mercator (UTM)
  291. template <typename Parameters, typename T>
  292. inline void setup_utm(Parameters& par, par_etmerc<T>& proj_parm)
  293. {
  294. static const T pi = detail::pi<T>();
  295. int zone;
  296. if (par.es == 0.0) {
  297. BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
  298. }
  299. par.y0 = pj_get_param_b(par.params, "south") ? 10000000. : 0.;
  300. par.x0 = 500000.;
  301. if (pj_param_i(par.params, "zone", zone)) /* zone input ? */
  302. {
  303. if (zone > 0 && zone <= 60)
  304. --zone;
  305. else {
  306. BOOST_THROW_EXCEPTION( projection_exception(error_invalid_utm_zone) );
  307. }
  308. }
  309. else /* nearest central meridian input */
  310. {
  311. zone = int_floor((adjlon(par.lam0) + pi) * 30. / pi);
  312. if (zone < 0)
  313. zone = 0;
  314. else if (zone >= 60)
  315. zone = 59;
  316. }
  317. par.lam0 = (zone + .5) * pi / 30. - pi;
  318. par.k0 = 0.9996;
  319. par.phi0 = 0.;
  320. setup(par, proj_parm);
  321. }
  322. }} // namespace detail::etmerc
  323. #endif // doxygen
  324. /*!
  325. \brief Extended Transverse Mercator projection
  326. \ingroup projections
  327. \tparam Geographic latlong point type
  328. \tparam Cartesian xy point type
  329. \tparam Parameters parameter type
  330. \par Projection characteristics
  331. - Cylindrical
  332. - Spheroid
  333. \par Projection parameters
  334. - lat_ts: Latitude of true scale
  335. - lat_0: Latitude of origin
  336. \par Example
  337. \image html ex_etmerc.gif
  338. */
  339. template <typename T, typename Parameters>
  340. struct etmerc_ellipsoid : public detail::etmerc::base_etmerc_ellipsoid<T, Parameters>
  341. {
  342. inline etmerc_ellipsoid(const Parameters& par) : detail::etmerc::base_etmerc_ellipsoid<T, Parameters>(par)
  343. {
  344. detail::etmerc::setup_etmerc(this->m_par, this->m_proj_parm);
  345. }
  346. };
  347. /*!
  348. \brief Universal Transverse Mercator (UTM) projection
  349. \ingroup projections
  350. \tparam Geographic latlong point type
  351. \tparam Cartesian xy point type
  352. \tparam Parameters parameter type
  353. \par Projection characteristics
  354. - Cylindrical
  355. - Spheroid
  356. \par Projection parameters
  357. - zone: UTM Zone (integer)
  358. - south: Denotes southern hemisphere UTM zone (boolean)
  359. \par Example
  360. \image html ex_utm.gif
  361. */
  362. template <typename T, typename Parameters>
  363. struct utm_ellipsoid : public detail::etmerc::base_etmerc_ellipsoid<T, Parameters>
  364. {
  365. inline utm_ellipsoid(const Parameters& par) : detail::etmerc::base_etmerc_ellipsoid<T, Parameters>(par)
  366. {
  367. detail::etmerc::setup_utm(this->m_par, this->m_proj_parm);
  368. }
  369. };
  370. #ifndef DOXYGEN_NO_DETAIL
  371. namespace detail
  372. {
  373. // Static projection
  374. BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::par4::etmerc, etmerc_ellipsoid, etmerc_ellipsoid)
  375. BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION(srs::par4::utm, utm_ellipsoid, utm_ellipsoid)
  376. // Factory entry(s)
  377. template <typename T, typename Parameters>
  378. class etmerc_entry : public detail::factory_entry<T, Parameters>
  379. {
  380. public :
  381. virtual base_v<T, Parameters>* create_new(const Parameters& par) const
  382. {
  383. return new base_v_fi<etmerc_ellipsoid<T, Parameters>, T, Parameters>(par);
  384. }
  385. };
  386. template <typename T, typename Parameters>
  387. class utm_entry : public detail::factory_entry<T, Parameters>
  388. {
  389. public :
  390. virtual base_v<T, Parameters>* create_new(const Parameters& par) const
  391. {
  392. return new base_v_fi<utm_ellipsoid<T, Parameters>, T, Parameters>(par);
  393. }
  394. };
  395. template <typename T, typename Parameters>
  396. inline void etmerc_init(detail::base_factory<T, Parameters>& factory)
  397. {
  398. factory.add_to_factory("etmerc", new etmerc_entry<T, Parameters>);
  399. factory.add_to_factory("utm", new utm_entry<T, Parameters>);
  400. }
  401. } // namespace detail
  402. #endif // doxygen
  403. } // namespace projections
  404. }} // namespace boost::geometry
  405. #endif // BOOST_GEOMETRY_PROJECTIONS_ETMERC_HPP