geodist.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*!
  2. * \file lib/gis/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 "Spheroidal Geodesics, Reference Systems, and Local
  8. * Geometry" U.S. Naval Oceanographic Office, p. 162 Engineering
  9. * 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-2009 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 Original author CERL
  21. */
  22. #include <math.h>
  23. #include <grass/gis.h>
  24. #include "pi.h"
  25. static struct state {
  26. double boa;
  27. double f;
  28. double ff64;
  29. double al;
  30. double t1, t2, t3, t4, t1r, t2r;
  31. } state;
  32. static struct state *st = &state;
  33. /*!
  34. * \brief Begin geodesic distance.
  35. *
  36. * Initializes the distance calculations for the ellipsoid with
  37. * semi-major axis <i>a</i> (in meters) and ellipsoid eccentricity squared
  38. * <i>e2</i>. It is used only for the latitude-longitude projection.
  39. *
  40. * <b>Note:</b> Must be called once to establish the ellipsoid.
  41. *
  42. * \param a semi-major axis in meters
  43. * \param e2 ellipsoid eccentricity
  44. */
  45. void G_begin_geodesic_distance(double a, double e2)
  46. {
  47. st->al = a;
  48. st->boa = sqrt(1 - e2);
  49. st->f = 1 - st->boa;
  50. st->ff64 = st->f * st->f / 64;
  51. }
  52. /*!
  53. * \brief Sets geodesic distance lat1.
  54. *
  55. * Set the first latitude.
  56. *
  57. * <b>Note:</b> Must be called first.
  58. *
  59. * \param lat1 first latitude
  60. * \return
  61. */
  62. void G_set_geodesic_distance_lat1(double lat1)
  63. {
  64. st->t1r = atan(st->boa * tan(Radians(lat1)));
  65. }
  66. /*!
  67. * \brief Sets geodesic distance lat2.
  68. *
  69. * Set the second latitude.
  70. *
  71. * <b>Note:</b> Must be called second.
  72. *
  73. * \param lat2 second latitidue
  74. */
  75. void G_set_geodesic_distance_lat2(double lat2)
  76. {
  77. double stm, ctm, sdtm, cdtm;
  78. double tm, dtm;
  79. st->t2r = atan(st->boa * tan(Radians(lat2)));
  80. tm = (st->t1r + st->t2r) / 2;
  81. dtm = (st->t2r - st->t1r) / 2;
  82. stm = sin(tm);
  83. ctm = cos(tm);
  84. sdtm = sin(dtm);
  85. cdtm = cos(dtm);
  86. st->t1 = stm * cdtm;
  87. st->t1 = st->t1 * st->t1 * 2;
  88. st->t2 = sdtm * ctm;
  89. st->t2 = st->t2 * st->t2 * 2;
  90. st->t3 = sdtm * sdtm;
  91. st->t4 = cdtm * cdtm - stm * stm;
  92. }
  93. /*!
  94. * \brief Calculates geodesic distance.
  95. *
  96. * Calculates the geodesic distance from <i>lon1,lat1</i> to
  97. * <i>lon2,lat2</i> in meters where <i>lat1</i> was the latitude
  98. * passed to G_set_geodesic_distance_latl() and <i>lat2</i> was the
  99. * latitude passed to G_set_geodesic_distance_lat2().
  100. *
  101. * \param lon1 first longitude
  102. * \param lon2 second longitude
  103. *
  104. * \return double distance in meters
  105. */
  106. double G_geodesic_distance_lon_to_lon(double lon1, double lon2)
  107. {
  108. double a, cd, d, e, /*dl, */
  109. q, sd, sdlmr, t, u, v, x, y;
  110. sdlmr = sin(Radians(lon2 - lon1) / 2);
  111. /* special case - shapiro */
  112. if (sdlmr == 0.0 && st->t1r == st->t2r)
  113. return 0.0;
  114. q = st->t3 + sdlmr * sdlmr * st->t4;
  115. /* special case - shapiro */
  116. if (q == 1.0)
  117. return M_PI * st->al;
  118. /* Mod: shapiro
  119. * cd=1-2q is ill-conditioned if q is small O(10**-23)
  120. * (for high lats? with lon1-lon2 < .25 degrees?)
  121. * the computation of cd = 1-2*q will give cd==1.0.
  122. * However, note that t=dl/sd is dl/sin(dl) which approaches 1 as dl->0.
  123. * So the first step is to compute a good value for sd without using sin()
  124. * and then check cd && q to see if we got cd==1.0 when we shouldn't.
  125. * Note that dl isn't used except to get t,
  126. * but both cd and sd are used later
  127. */
  128. /* original code
  129. cd=1-2*q;
  130. dl=acos(cd);
  131. sd=sin(dl);
  132. t=dl/sd;
  133. */
  134. cd = 1 - 2 * q; /* ill-conditioned subtraction for small q */
  135. /* mod starts here */
  136. sd = 2 * sqrt(q - q * q); /* sd^2 = 1 - cd^2 */
  137. if (q != 0.0 && cd == 1.0) /* test for small q */
  138. t = 1.0;
  139. else if (sd == 0.0)
  140. t = 1.0;
  141. else
  142. t = acos(cd) / sd; /* don't know how to fix acos(1-2*q) yet */
  143. /* mod ends here */
  144. u = st->t1 / (1 - q);
  145. v = st->t2 / q;
  146. d = 4 * t * t;
  147. x = u + v;
  148. e = -2 * cd;
  149. y = u - v;
  150. a = -d * e;
  151. return st->al * sd * (t
  152. - st->f / 4 * (t * x - y)
  153. + st->ff64 * (x * (a + (t - (a + e) / 2) * x)
  154. + y * (-2 * d + e * y) + d * x * y));
  155. }
  156. /*!
  157. * \brief Calculates geodesic distance.
  158. *
  159. * Calculates the geodesic distance from <i>lon1,lat1</i> to
  160. * <i>lon2,lat2</i> in meters.
  161. *
  162. * <b>Note:</b> The calculation of the geodesic distance is fairly
  163. * costly.
  164. *
  165. * \param lon1,lat1 longitude,latitude of first point
  166. * \param lon2,lat2 longitude,latitude of second point
  167. *
  168. * \return distance in meters
  169. */
  170. double G_geodesic_distance(double lon1, double lat1, double lon2, double lat2)
  171. {
  172. G_set_geodesic_distance_lat1(lat1);
  173. G_set_geodesic_distance_lat2(lat2);
  174. return G_geodesic_distance_lon_to_lon(lon1, lon2);
  175. }