geocent.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. // Boost.Geometry
  2. // This file is manually converted from PROJ4
  3. // This file was modified by Oracle on 2017.
  4. // Modifications copyright (c) 2017, 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. /***************************************************************************/
  15. /* RSC IDENTIFIER: GEOCENTRIC
  16. *
  17. * ABSTRACT
  18. *
  19. * This component provides conversions between Geodetic coordinates (latitude,
  20. * longitude in radians and height in meters) and Geocentric coordinates
  21. * (X, Y, Z) in meters.
  22. *
  23. * ERROR HANDLING
  24. *
  25. * This component checks parameters for valid values. If an invalid value
  26. * is found, the error code is combined with the current error code using
  27. * the bitwise or. This combining allows multiple error codes to be
  28. * returned. The possible error codes are:
  29. *
  30. * GEOCENT_NO_ERROR : No errors occurred in function
  31. * GEOCENT_LAT_ERROR : Latitude out of valid range
  32. * (-90 to 90 degrees)
  33. * GEOCENT_LON_ERROR : Longitude out of valid range
  34. * (-180 to 360 degrees)
  35. * GEOCENT_A_ERROR : Semi-major axis lessthan or equal to zero
  36. * GEOCENT_B_ERROR : Semi-minor axis lessthan or equal to zero
  37. * GEOCENT_A_LESS_B_ERROR : Semi-major axis less than semi-minor axis
  38. *
  39. *
  40. * REUSE NOTES
  41. *
  42. * GEOCENTRIC is intended for reuse by any application that performs
  43. * coordinate conversions between geodetic coordinates and geocentric
  44. * coordinates.
  45. *
  46. *
  47. * REFERENCES
  48. *
  49. * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion,
  50. * Ralph Toms, February 1996 UCRL-JC-123138.
  51. *
  52. * Further information on GEOCENTRIC can be found in the Reuse Manual.
  53. *
  54. * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center
  55. * Geospatial Information Division
  56. * 7701 Telegraph Road
  57. * Alexandria, VA 22310-3864
  58. *
  59. * LICENSES
  60. *
  61. * None apply to this component.
  62. *
  63. * RESTRICTIONS
  64. *
  65. * GEOCENTRIC has no restrictions.
  66. *
  67. * ENVIRONMENT
  68. *
  69. * GEOCENTRIC was tested and certified in the following environments:
  70. *
  71. * 1. Solaris 2.5 with GCC version 2.8.1
  72. * 2. Windows 95 with MS Visual C++ version 6
  73. *
  74. * MODIFICATIONS
  75. *
  76. * Date Description
  77. * ---- -----------
  78. * 25-02-97 Original Code
  79. *
  80. */
  81. #ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_GEOCENT_HPP
  82. #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_GEOCENT_HPP
  83. #include <boost/geometry/util/math.hpp>
  84. namespace boost { namespace geometry { namespace projections
  85. {
  86. namespace detail
  87. {
  88. /***************************************************************************/
  89. /*
  90. * DEFINES
  91. */
  92. static const long GEOCENT_NO_ERROR = 0x0000;
  93. static const long GEOCENT_LAT_ERROR = 0x0001;
  94. static const long GEOCENT_LON_ERROR = 0x0002;
  95. static const long GEOCENT_A_ERROR = 0x0004;
  96. static const long GEOCENT_B_ERROR = 0x0008;
  97. static const long GEOCENT_A_LESS_B_ERROR = 0x0010;
  98. template <typename T>
  99. struct GeocentricInfo
  100. {
  101. T Geocent_a; /* Semi-major axis of ellipsoid in meters */
  102. T Geocent_b; /* Semi-minor axis of ellipsoid */
  103. T Geocent_a2; /* Square of semi-major axis */
  104. T Geocent_b2; /* Square of semi-minor axis */
  105. T Geocent_e2; /* Eccentricity squared */
  106. T Geocent_ep2; /* 2nd eccentricity squared */
  107. };
  108. template <typename T>
  109. inline T COS_67P5()
  110. {
  111. /*return 0.38268343236508977*/;
  112. return cos(T(67.5) * math::d2r<T>()); /* cosine of 67.5 degrees */
  113. }
  114. template <typename T>
  115. inline T AD_C()
  116. {
  117. return 1.0026000; /* Toms region 1 constant */
  118. }
  119. /***************************************************************************/
  120. /*
  121. * FUNCTIONS
  122. */
  123. template <typename T>
  124. inline long pj_Set_Geocentric_Parameters (GeocentricInfo<T> & gi, T const& a, T const& b)
  125. { /* BEGIN Set_Geocentric_Parameters */
  126. /*
  127. * The function Set_Geocentric_Parameters receives the ellipsoid parameters
  128. * as inputs and sets the corresponding state variables.
  129. *
  130. * a : Semi-major axis, in meters. (input)
  131. * b : Semi-minor axis, in meters. (input)
  132. */
  133. long Error_Code = GEOCENT_NO_ERROR;
  134. if (a <= 0.0)
  135. Error_Code |= GEOCENT_A_ERROR;
  136. if (b <= 0.0)
  137. Error_Code |= GEOCENT_B_ERROR;
  138. if (a < b)
  139. Error_Code |= GEOCENT_A_LESS_B_ERROR;
  140. if (!Error_Code)
  141. {
  142. gi.Geocent_a = a;
  143. gi.Geocent_b = b;
  144. gi.Geocent_a2 = a * a;
  145. gi.Geocent_b2 = b * b;
  146. gi.Geocent_e2 = (gi.Geocent_a2 - gi.Geocent_b2) / gi.Geocent_a2;
  147. gi.Geocent_ep2 = (gi.Geocent_a2 - gi.Geocent_b2) / gi.Geocent_b2;
  148. }
  149. return (Error_Code);
  150. } /* END OF Set_Geocentric_Parameters */
  151. template <typename T>
  152. inline void pj_Get_Geocentric_Parameters (GeocentricInfo<T> const& gi,
  153. T & a,
  154. T & b)
  155. { /* BEGIN Get_Geocentric_Parameters */
  156. /*
  157. * The function Get_Geocentric_Parameters returns the ellipsoid parameters
  158. * to be used in geocentric coordinate conversions.
  159. *
  160. * a : Semi-major axis, in meters. (output)
  161. * b : Semi-minor axis, in meters. (output)
  162. */
  163. a = gi.Geocent_a;
  164. b = gi.Geocent_b;
  165. } /* END OF Get_Geocentric_Parameters */
  166. template <typename T>
  167. inline long pj_Convert_Geodetic_To_Geocentric (GeocentricInfo<T> const& gi,
  168. T Longitude, T Latitude, T Height,
  169. T & X, T & Y, T & Z)
  170. { /* BEGIN Convert_Geodetic_To_Geocentric */
  171. /*
  172. * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates
  173. * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
  174. * according to the current ellipsoid parameters.
  175. *
  176. * Latitude : Geodetic latitude in radians (input)
  177. * Longitude : Geodetic longitude in radians (input)
  178. * Height : Geodetic height, in meters (input)
  179. * X : Calculated Geocentric X coordinate, in meters (output)
  180. * Y : Calculated Geocentric Y coordinate, in meters (output)
  181. * Z : Calculated Geocentric Z coordinate, in meters (output)
  182. *
  183. */
  184. long Error_Code = GEOCENT_NO_ERROR;
  185. T Rn; /* Earth radius at location */
  186. T Sin_Lat; /* sin(Latitude) */
  187. T Sin2_Lat; /* Square of sin(Latitude) */
  188. T Cos_Lat; /* cos(Latitude) */
  189. static const T PI = math::pi<T>();
  190. static const T PI_OVER_2 = math::half_pi<T>();
  191. /*
  192. ** Don't blow up if Latitude is just a little out of the value
  193. ** range as it may just be a rounding issue. Also removed longitude
  194. ** test, it should be wrapped by cos() and sin(). NFW for PROJ.4, Sep/2001.
  195. */
  196. if( Latitude < -PI_OVER_2 && Latitude > -1.001 * PI_OVER_2 )
  197. Latitude = -PI_OVER_2;
  198. else if( Latitude > PI_OVER_2 && Latitude < 1.001 * PI_OVER_2 )
  199. Latitude = PI_OVER_2;
  200. else if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2))
  201. { /* Latitude out of range */
  202. Error_Code |= GEOCENT_LAT_ERROR;
  203. }
  204. if (!Error_Code)
  205. { /* no errors */
  206. if (Longitude > PI)
  207. Longitude -= (2*PI);
  208. Sin_Lat = sin(Latitude);
  209. Cos_Lat = cos(Latitude);
  210. Sin2_Lat = Sin_Lat * Sin_Lat;
  211. Rn = gi.Geocent_a / (sqrt(1.0e0 - gi.Geocent_e2 * Sin2_Lat));
  212. X = (Rn + Height) * Cos_Lat * cos(Longitude);
  213. Y = (Rn + Height) * Cos_Lat * sin(Longitude);
  214. Z = ((Rn * (1 - gi.Geocent_e2)) + Height) * Sin_Lat;
  215. }
  216. return (Error_Code);
  217. } /* END OF Convert_Geodetic_To_Geocentric */
  218. /*
  219. * The function Convert_Geocentric_To_Geodetic converts geocentric
  220. * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude,
  221. * and height), according to the current ellipsoid parameters.
  222. *
  223. * X : Geocentric X coordinate, in meters. (input)
  224. * Y : Geocentric Y coordinate, in meters. (input)
  225. * Z : Geocentric Z coordinate, in meters. (input)
  226. * Latitude : Calculated latitude value in radians. (output)
  227. * Longitude : Calculated longitude value in radians. (output)
  228. * Height : Calculated height value, in meters. (output)
  229. */
  230. #define BOOST_GEOMETRY_PROJECTIONS_USE_ITERATIVE_METHOD
  231. template <typename T>
  232. inline void pj_Convert_Geocentric_To_Geodetic (GeocentricInfo<T> const& gi,
  233. T X, T Y, T Z,
  234. T & Longitude, T & Latitude, T & Height)
  235. { /* BEGIN Convert_Geocentric_To_Geodetic */
  236. static const T PI_OVER_2 = math::half_pi<T>();
  237. #if !defined(BOOST_GEOMETRY_PROJECTIONS_USE_ITERATIVE_METHOD)
  238. static const T COS_67P5 = detail::COS_67P5<T>();
  239. static const T AD_C = detail::AD_C<T>();
  240. /*
  241. * The method used here is derived from 'An Improved Algorithm for
  242. * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
  243. */
  244. /* Note: Variable names follow the notation used in Toms, Feb 1996 */
  245. T W; /* distance from Z axis */
  246. T W2; /* square of distance from Z axis */
  247. T T0; /* initial estimate of vertical component */
  248. T T1; /* corrected estimate of vertical component */
  249. T S0; /* initial estimate of horizontal component */
  250. T S1; /* corrected estimate of horizontal component */
  251. T Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */
  252. T Sin3_B0; /* cube of sin(B0) */
  253. T Cos_B0; /* cos(B0) */
  254. T Sin_p1; /* sin(phi1), phi1 is estimated latitude */
  255. T Cos_p1; /* cos(phi1) */
  256. T Rn; /* Earth radius at location */
  257. T Sum; /* numerator of cos(phi1) */
  258. bool At_Pole; /* indicates location is in polar region */
  259. At_Pole = false;
  260. if (X != 0.0)
  261. {
  262. Longitude = atan2(Y,X);
  263. }
  264. else
  265. {
  266. if (Y > 0)
  267. {
  268. Longitude = PI_OVER_2;
  269. }
  270. else if (Y < 0)
  271. {
  272. Longitude = -PI_OVER_2;
  273. }
  274. else
  275. {
  276. At_Pole = true;
  277. Longitude = 0.0;
  278. if (Z > 0.0)
  279. { /* north pole */
  280. Latitude = PI_OVER_2;
  281. }
  282. else if (Z < 0.0)
  283. { /* south pole */
  284. Latitude = -PI_OVER_2;
  285. }
  286. else
  287. { /* center of earth */
  288. Latitude = PI_OVER_2;
  289. Height = -Geocent_b;
  290. return;
  291. }
  292. }
  293. }
  294. W2 = X*X + Y*Y;
  295. W = sqrt(W2);
  296. T0 = Z * AD_C;
  297. S0 = sqrt(T0 * T0 + W2);
  298. Sin_B0 = T0 / S0;
  299. Cos_B0 = W / S0;
  300. Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
  301. T1 = Z + gi.Geocent_b * gi.Geocent_ep2 * Sin3_B0;
  302. Sum = W - gi.Geocent_a * gi.Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0;
  303. S1 = sqrt(T1*T1 + Sum * Sum);
  304. Sin_p1 = T1 / S1;
  305. Cos_p1 = Sum / S1;
  306. Rn = gi.Geocent_a / sqrt(1.0 - gi.Geocent_e2 * Sin_p1 * Sin_p1);
  307. if (Cos_p1 >= COS_67P5)
  308. {
  309. Height = W / Cos_p1 - Rn;
  310. }
  311. else if (Cos_p1 <= -COS_67P5)
  312. {
  313. Height = W / -Cos_p1 - Rn;
  314. }
  315. else
  316. {
  317. Height = Z / Sin_p1 + Rn * (gi.Geocent_e2 - 1.0);
  318. }
  319. if (At_Pole == false)
  320. {
  321. Latitude = atan(Sin_p1 / Cos_p1);
  322. }
  323. #else /* defined(BOOST_GEOMETRY_PROJECTIONS_USE_ITERATIVE_METHOD) */
  324. /*
  325. * Reference...
  326. * ============
  327. * Wenzel, H.-G.(1985): Hochauflösende Kugelfunktionsmodelle für
  328. * das Gravitationspotential der Erde. Wiss. Arb. Univ. Hannover
  329. * Nr. 137, p. 130-131.
  330. * Programmed by GGA- Leibniz-Institute of Applied Geophysics
  331. * Stilleweg 2
  332. * D-30655 Hannover
  333. * Federal Republic of Germany
  334. * Internet: www.gga-hannover.de
  335. *
  336. * Hannover, March 1999, April 2004.
  337. * see also: comments in statements
  338. * remarks:
  339. * Mathematically exact and because of symmetry of rotation-ellipsoid,
  340. * each point (X,Y,Z) has at least two solutions (Latitude1,Longitude1,Height1) and
  341. * (Latitude2,Longitude2,Height2). Is point=(0.,0.,Z) (P=0.), so you get even
  342. * four solutions, every two symmetrical to the semi-minor axis.
  343. * Here Height1 and Height2 have at least a difference in order of
  344. * radius of curvature (e.g. (0,0,b)=> (90.,0.,0.) or (-90.,0.,-2b);
  345. * (a+100.)*(sqrt(2.)/2.,sqrt(2.)/2.,0.) => (0.,45.,100.) or
  346. * (0.,225.,-(2a+100.))).
  347. * The algorithm always computes (Latitude,Longitude) with smallest |Height|.
  348. * For normal computations, that means |Height|<10000.m, algorithm normally
  349. * converges after to 2-3 steps!!!
  350. * But if |Height| has the amount of length of ellipsoid's axis
  351. * (e.g. -6300000.m), algorithm needs about 15 steps.
  352. */
  353. /* local definitions and variables */
  354. /* end-criterium of loop, accuracy of sin(Latitude) */
  355. static const T genau = 1.E-12;
  356. static const T genau2 = (genau*genau);
  357. static const int maxiter = 30;
  358. T P; /* distance between semi-minor axis and location */
  359. T RR; /* distance between center and location */
  360. T CT; /* sin of geocentric latitude */
  361. T ST; /* cos of geocentric latitude */
  362. T RX;
  363. T RK;
  364. T RN; /* Earth radius at location */
  365. T CPHI0; /* cos of start or old geodetic latitude in iterations */
  366. T SPHI0; /* sin of start or old geodetic latitude in iterations */
  367. T CPHI; /* cos of searched geodetic latitude */
  368. T SPHI; /* sin of searched geodetic latitude */
  369. T SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */
  370. int iter; /* # of continuous iteration, max. 30 is always enough (s.a.) */
  371. P = sqrt(X*X+Y*Y);
  372. RR = sqrt(X*X+Y*Y+Z*Z);
  373. /* special cases for latitude and longitude */
  374. if (P/gi.Geocent_a < genau) {
  375. /* special case, if P=0. (X=0., Y=0.) */
  376. Longitude = 0.;
  377. /* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis
  378. * of ellipsoid (=center of mass), Latitude becomes PI/2 */
  379. if (RR/gi.Geocent_a < genau) {
  380. Latitude = PI_OVER_2;
  381. Height = -gi.Geocent_b;
  382. return ;
  383. }
  384. }
  385. else {
  386. /* ellipsoidal (geodetic) longitude
  387. * interval: -PI < Longitude <= +PI */
  388. Longitude=atan2(Y,X);
  389. }
  390. /* --------------------------------------------------------------
  391. * Following iterative algorithm was developed by
  392. * "Institut für Erdmessung", University of Hannover, July 1988.
  393. * Internet: www.ife.uni-hannover.de
  394. * Iterative computation of CPHI,SPHI and Height.
  395. * Iteration of CPHI and SPHI to 10**-12 radian resp.
  396. * 2*10**-7 arcsec.
  397. * --------------------------------------------------------------
  398. */
  399. CT = Z/RR;
  400. ST = P/RR;
  401. RX = 1.0/sqrt(1.0-gi.Geocent_e2*(2.0-gi.Geocent_e2)*ST*ST);
  402. CPHI0 = ST*(1.0-gi.Geocent_e2)*RX;
  403. SPHI0 = CT*RX;
  404. iter = 0;
  405. /* loop to find sin(Latitude) resp. Latitude
  406. * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */
  407. do
  408. {
  409. iter++;
  410. RN = gi.Geocent_a/sqrt(1.0-gi.Geocent_e2*SPHI0*SPHI0);
  411. /* ellipsoidal (geodetic) height */
  412. Height = P*CPHI0+Z*SPHI0-RN*(1.0-gi.Geocent_e2*SPHI0*SPHI0);
  413. RK = gi.Geocent_e2*RN/(RN+Height);
  414. RX = 1.0/sqrt(1.0-RK*(2.0-RK)*ST*ST);
  415. CPHI = ST*(1.0-RK)*RX;
  416. SPHI = CT*RX;
  417. SDPHI = SPHI*CPHI0-CPHI*SPHI0;
  418. CPHI0 = CPHI;
  419. SPHI0 = SPHI;
  420. }
  421. while (SDPHI*SDPHI > genau2 && iter < maxiter);
  422. /* ellipsoidal (geodetic) latitude */
  423. Latitude=atan(SPHI/fabs(CPHI));
  424. return;
  425. #endif /* defined(BOOST_GEOMETRY_PROJECTIONS_USE_ITERATIVE_METHOD) */
  426. } /* END OF Convert_Geocentric_To_Geodetic */
  427. } // namespace detail
  428. }}} // namespace boost::geometry::projections
  429. #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_GEOCENT_HPP