geodist.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /**
  2. * \file geodist.c
  3. *
  4. * \brief GIS Library - Geodesic distance routines.
  5. *
  6. * Distance from point to point along a geodesic code from Paul
  7. * D. Thomas, 1970<br> "Spheroidal Geodesics, Reference Systems, and
  8. * Local Geometry"<br> U.S. Naval Oceanographic Office, p. 162<br>
  9. * Engineering Library 526.3 T36s
  10. * http://stinet.dtic.mil/oai/oai?&verb=getRecord&metadataPrefix=html&identifier=AD0703541
  11. *
  12. * <b>WARNING:</b> this code is preliminary and may be changed,
  13. * including calling sequences to any of the functions defined here.
  14. *
  15. * (C) 2001-2008 by the GRASS Development Team
  16. *
  17. * This program is free software under the GNU General Public License
  18. * (>=v2). Read the file COPYING that comes with GRASS for details.
  19. *
  20. * \author GRASS GIS Development Team
  21. *
  22. * \date 1999-2008
  23. */
  24. #include <math.h>
  25. #include <grass/gis.h>
  26. #include "pi.h"
  27. static struct state {
  28. double boa;
  29. double f;
  30. double ff64;
  31. double al;
  32. double t1, t2, t3, t4, t1r, t2r;
  33. } state;
  34. static struct state *st = &state;
  35. /**
  36. * \brief Begin geodesic distance.
  37. *
  38. * Initializes the distance calculations for the ellipsoid with
  39. * semi-major axis <b>a</b> (in meters) and ellipsoid eccentricity squared
  40. * <b>e2</b>. It is used only for the latitude-longitude projection.
  41. * <br>
  42. * <b>Note:</b> Must be called once to establish the ellipsoid.
  43. *
  44. * \param[in] a semi-major axis in meters
  45. * \param[in] e2 ellipsoid eccentricity
  46. * \return always returns 0
  47. */
  48. int G_begin_geodesic_distance(double a, double e2)
  49. {
  50. st->al = a;
  51. st->boa = sqrt(1 - e2);
  52. st->f = 1 - st->boa;
  53. st->ff64 = st->f * st->f / 64;
  54. return 0;
  55. }
  56. /**
  57. * \brief Sets geodesic distance lat1.
  58. *
  59. * Set the first latitude.
  60. *
  61. * <b>Note:</b> Must be called first.
  62. *
  63. * \param[in] lat1 first latitude
  64. * \return always returns 0
  65. */
  66. int G_set_geodesic_distance_lat1(double lat1)
  67. {
  68. st->t1r = atan(st->boa * tan(Radians(lat1)));
  69. return 0;
  70. }
  71. /**
  72. * \brief Sets geodesic distance lat2.
  73. *
  74. * Set the second latitude.
  75. *
  76. * <b>Note:</b> Must be called second.
  77. *
  78. * \param[in] lat2 second latitidue
  79. * \return always returns 0
  80. */
  81. int G_set_geodesic_distance_lat2(double lat2)
  82. {
  83. double stm, ctm, sdtm, cdtm;
  84. double tm, dtm;
  85. st->t2r = atan(st->boa * tan(Radians(lat2)));
  86. tm = (st->t1r + st->t2r) / 2;
  87. dtm = (st->t2r - st->t1r) / 2;
  88. stm = sin(tm);
  89. ctm = cos(tm);
  90. sdtm = sin(dtm);
  91. cdtm = cos(dtm);
  92. st->t1 = stm * cdtm;
  93. st->t1 = st->t1 * st->t1 * 2;
  94. st->t2 = sdtm * ctm;
  95. st->t2 = st->t2 * st->t2 * 2;
  96. st->t3 = sdtm * sdtm;
  97. st->t4 = cdtm * cdtm - stm * stm;
  98. return 0;
  99. }
  100. /**
  101. * \brief Calculates geodesic distance.
  102. *
  103. * Calculates the geodesic distance from <b>lon1,lat1</b> to
  104. * <b>lon2,lat2</b> in meters where <b>lat1</b> was the latitude passed
  105. * to <i>G_set_geodesic_distance_latl()</i> and <b>lat2</b> was the
  106. * latitude passed to <i>G_set_geodesic_distance_lat2()</i>.
  107. *
  108. * \param[in] lon1 first longitude
  109. * \param[in] lon2 second longitude
  110. * \return double distance in meters
  111. */
  112. double G_geodesic_distance_lon_to_lon(double lon1, double lon2)
  113. {
  114. double a, cd, d, e, /*dl, */
  115. q, sd, sdlmr, t, u, v, x, y;
  116. sdlmr = sin(Radians(lon2 - lon1) / 2);
  117. /* special case - shapiro */
  118. if (sdlmr == 0.0 && st->t1r == st->t2r)
  119. return 0.0;
  120. q = st->t3 + sdlmr * sdlmr * st->t4;
  121. /* special case - shapiro */
  122. if (q == 1.0)
  123. return M_PI * st->al;
  124. /* Mod: shapiro
  125. * cd=1-2q is ill-conditioned if q is small O(10**-23)
  126. * (for high lats? with lon1-lon2 < .25 degrees?)
  127. * the computation of cd = 1-2*q will give cd==1.0.
  128. * However, note that t=dl/sd is dl/sin(dl) which approaches 1 as dl->0.
  129. * So the first step is to compute a good value for sd without using sin()
  130. * and then check cd && q to see if we got cd==1.0 when we shouldn't.
  131. * Note that dl isn't used except to get t,
  132. * but both cd and sd are used later
  133. */
  134. /* original code
  135. cd=1-2*q;
  136. dl=acos(cd);
  137. sd=sin(dl);
  138. t=dl/sd;
  139. */
  140. cd = 1 - 2 * q; /* ill-conditioned subtraction for small q */
  141. /* mod starts here */
  142. sd = 2 * sqrt(q - q * q); /* sd^2 = 1 - cd^2 */
  143. if (q != 0.0 && cd == 1.0) /* test for small q */
  144. t = 1.0;
  145. else if (sd == 0.0)
  146. t = 1.0;
  147. else
  148. t = acos(cd) / sd; /* don't know how to fix acos(1-2*q) yet */
  149. /* mod ends here */
  150. u = st->t1 / (1 - q);
  151. v = st->t2 / q;
  152. d = 4 * t * t;
  153. x = u + v;
  154. e = -2 * cd;
  155. y = u - v;
  156. a = -d * e;
  157. return st->al * sd * (t
  158. - st->f / 4 * (t * x - y)
  159. + st->ff64 * (x * (a + (t - (a + e) / 2) * x)
  160. + y * (-2 * d + e * y) + d * x * y));
  161. }
  162. /**
  163. * \brief Calculates geodesic distance.
  164. *
  165. * Calculates the geodesic distance from <b>lon1,lat1</b> to
  166. * <b>lon2,lat2</b> in meters.
  167. * <br>
  168. * <b>Note:</b> The calculation of the geodesic distance is fairly costly.
  169. *
  170. * \param[in] lon1,lat1 longitude,latitude of first point
  171. * \param[in] lon2,lat2 longitude,latitude of second point
  172. * \return distance in meters
  173. */
  174. double G_geodesic_distance(double lon1, double lat1, double lon2, double lat2)
  175. {
  176. G_set_geodesic_distance_lat1(lat1);
  177. G_set_geodesic_distance_lat2(lat2);
  178. return G_geodesic_distance_lon_to_lon(lon1, lon2);
  179. }