geodist.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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
  47. */
  48. void 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. }
  55. /**
  56. * \brief Sets geodesic distance lat1.
  57. *
  58. * Set the first latitude.
  59. *
  60. * <b>Note:</b> Must be called first.
  61. *
  62. * \param[in] lat1 first latitude
  63. * \return
  64. */
  65. void G_set_geodesic_distance_lat1(double lat1)
  66. {
  67. st->t1r = atan(st->boa * tan(Radians(lat1)));
  68. }
  69. /**
  70. * \brief Sets geodesic distance lat2.
  71. *
  72. * Set the second latitude.
  73. *
  74. * <b>Note:</b> Must be called second.
  75. *
  76. * \param[in] lat2 second latitidue
  77. * \return
  78. */
  79. void G_set_geodesic_distance_lat2(double lat2)
  80. {
  81. double stm, ctm, sdtm, cdtm;
  82. double tm, dtm;
  83. st->t2r = atan(st->boa * tan(Radians(lat2)));
  84. tm = (st->t1r + st->t2r) / 2;
  85. dtm = (st->t2r - st->t1r) / 2;
  86. stm = sin(tm);
  87. ctm = cos(tm);
  88. sdtm = sin(dtm);
  89. cdtm = cos(dtm);
  90. st->t1 = stm * cdtm;
  91. st->t1 = st->t1 * st->t1 * 2;
  92. st->t2 = sdtm * ctm;
  93. st->t2 = st->t2 * st->t2 * 2;
  94. st->t3 = sdtm * sdtm;
  95. st->t4 = cdtm * cdtm - stm * stm;
  96. }
  97. /**
  98. * \brief Calculates geodesic distance.
  99. *
  100. * Calculates the geodesic distance from <b>lon1,lat1</b> to
  101. * <b>lon2,lat2</b> in meters where <b>lat1</b> was the latitude passed
  102. * to <i>G_set_geodesic_distance_latl()</i> and <b>lat2</b> was the
  103. * latitude passed to <i>G_set_geodesic_distance_lat2()</i>.
  104. *
  105. * \param[in] lon1 first longitude
  106. * \param[in] lon2 second longitude
  107. * \return double distance in meters
  108. */
  109. double G_geodesic_distance_lon_to_lon(double lon1, double lon2)
  110. {
  111. double a, cd, d, e, /*dl, */
  112. q, sd, sdlmr, t, u, v, x, y;
  113. sdlmr = sin(Radians(lon2 - lon1) / 2);
  114. /* special case - shapiro */
  115. if (sdlmr == 0.0 && st->t1r == st->t2r)
  116. return 0.0;
  117. q = st->t3 + sdlmr * sdlmr * st->t4;
  118. /* special case - shapiro */
  119. if (q == 1.0)
  120. return M_PI * st->al;
  121. /* Mod: shapiro
  122. * cd=1-2q is ill-conditioned if q is small O(10**-23)
  123. * (for high lats? with lon1-lon2 < .25 degrees?)
  124. * the computation of cd = 1-2*q will give cd==1.0.
  125. * However, note that t=dl/sd is dl/sin(dl) which approaches 1 as dl->0.
  126. * So the first step is to compute a good value for sd without using sin()
  127. * and then check cd && q to see if we got cd==1.0 when we shouldn't.
  128. * Note that dl isn't used except to get t,
  129. * but both cd and sd are used later
  130. */
  131. /* original code
  132. cd=1-2*q;
  133. dl=acos(cd);
  134. sd=sin(dl);
  135. t=dl/sd;
  136. */
  137. cd = 1 - 2 * q; /* ill-conditioned subtraction for small q */
  138. /* mod starts here */
  139. sd = 2 * sqrt(q - q * q); /* sd^2 = 1 - cd^2 */
  140. if (q != 0.0 && cd == 1.0) /* test for small q */
  141. t = 1.0;
  142. else if (sd == 0.0)
  143. t = 1.0;
  144. else
  145. t = acos(cd) / sd; /* don't know how to fix acos(1-2*q) yet */
  146. /* mod ends here */
  147. u = st->t1 / (1 - q);
  148. v = st->t2 / q;
  149. d = 4 * t * t;
  150. x = u + v;
  151. e = -2 * cd;
  152. y = u - v;
  153. a = -d * e;
  154. return st->al * sd * (t
  155. - st->f / 4 * (t * x - y)
  156. + st->ff64 * (x * (a + (t - (a + e) / 2) * x)
  157. + y * (-2 * d + e * y) + d * x * y));
  158. }
  159. /**
  160. * \brief Calculates geodesic distance.
  161. *
  162. * Calculates the geodesic distance from <b>lon1,lat1</b> to
  163. * <b>lon2,lat2</b> in meters.
  164. * <br>
  165. * <b>Note:</b> The calculation of the geodesic distance is fairly costly.
  166. *
  167. * \param[in] lon1,lat1 longitude,latitude of first point
  168. * \param[in] lon2,lat2 longitude,latitude of second point
  169. * \return distance in meters
  170. */
  171. double G_geodesic_distance(double lon1, double lat1, double lon2, double lat2)
  172. {
  173. G_set_geodesic_distance_lat1(lat1);
  174. G_set_geodesic_distance_lat2(lat2);
  175. return G_geodesic_distance_lon_to_lon(lon1, lon2);
  176. }