distance.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*!
  2. * \file gis/distance.c
  3. *
  4. * \brief GIS Library - Distance calculation functions.
  5. *
  6. * WARNING: this code is preliminary and may be changed,
  7. * including calling sequences to any of the functions
  8. * defined here.
  9. *
  10. * (C) 2001-2009 by the GRASS Development Team
  11. *
  12. * This program is free software under the GNU General Public License
  13. * (>=v2). Read the file COPYING that comes with GRASS for details.
  14. *
  15. * \author Original author CERL
  16. */
  17. #include <math.h>
  18. #include <grass/gis.h>
  19. #include <grass/glocale.h>
  20. static double min4(double, double, double, double);
  21. static double min2(double, double);
  22. static struct state {
  23. int projection;
  24. double factor;
  25. } state;
  26. static struct state *st = &state;
  27. /*!
  28. * \brief Begin distance calculations.
  29. *
  30. * Initializes the distance calculations. It is used both for the
  31. * planimetric and latitude-longitude projections.
  32. *
  33. * \return 0 if projection has no metrix (ie. imagery)
  34. * \return 1 if projection is planimetric
  35. * \return 2 if projection is latitude-longitude
  36. */
  37. int G_begin_distance_calculations(void)
  38. {
  39. double a, e2;
  40. st->factor = 1.0;
  41. switch (st->projection = G_projection()) {
  42. case PROJECTION_LL:
  43. G_get_ellipsoid_parameters(&a, &e2);
  44. G_begin_geodesic_distance(a, e2);
  45. return 2;
  46. default:
  47. st->factor = G_database_units_to_meters_factor();
  48. if (st->factor <= 0.0) {
  49. st->factor = 1.0; /* assume meter grid */
  50. return 0;
  51. }
  52. return 1;
  53. }
  54. }
  55. /*!
  56. * \brief Returns distance in meters.
  57. *
  58. * This routine computes the distance, in meters, from
  59. * <i>x1</i>,<i>y1</i> to <i>x2</i>,<i>y2</i>. If the projection is
  60. * latitude-longitude, this distance is measured along the
  61. * geodesic. Two routines perform geodesic distance calculations.
  62. *
  63. * \param e1,n1 east-north coordinates of first point
  64. * \param e2,n2 east-north coordinates of second point
  65. * \return distance
  66. */
  67. double G_distance(double e1, double n1, double e2, double n2)
  68. {
  69. if (st->projection == PROJECTION_LL)
  70. return G_geodesic_distance(e1, n1, e2, n2);
  71. else
  72. return st->factor * hypot(e1 - e2, n1 - n2);
  73. }
  74. /*!
  75. * \brief Returns distance between two line segments in meters.
  76. *
  77. * \param ax1,ay2,ax2,ay2 first segment
  78. * \param bx1,by2,bx2,by2 second segment
  79. *
  80. * \return distance value
  81. */
  82. double G_distance_between_line_segments(double ax1, double ay1,
  83. double ax2, double ay2,
  84. double bx1, double by1,
  85. double bx2, double by2)
  86. {
  87. double ra, rb;
  88. double x, y;
  89. /* if the segments intersect, then the distance is zero */
  90. if (G_intersect_line_segments(ax1, ay1, ax2, ay2,
  91. bx1, by1, bx2, by2, &ra, &rb, &x, &y) > 0)
  92. return 0.0;
  93. return
  94. min4(G_distance_point_to_line_segment(ax1, ay1, bx1, by1, bx2, by2),
  95. G_distance_point_to_line_segment(ax2, ay2, bx1, by1, bx2, by2),
  96. G_distance_point_to_line_segment(bx1, by1, ax1, ay1, ax2, ay2),
  97. G_distance_point_to_line_segment(bx2, by2, ax1, ay1, ax2, ay2)
  98. );
  99. }
  100. /*!
  101. * \brief Returns distance between a point and line segment in meters.
  102. *
  103. * \param xp,yp point coordinates
  104. * \param x1,x1 segment point coordinates
  105. * \param x2,y2 segment point coordinates
  106. *
  107. * \return distance
  108. */
  109. double G_distance_point_to_line_segment(double xp, double yp,
  110. double x1, double y1, double x2,
  111. double y2)
  112. {
  113. double dx, dy;
  114. double x, y;
  115. double xq, yq, ra, rb;
  116. int t;
  117. /* define the perpendicular to the segment thru the point */
  118. dx = x1 - x2;
  119. dy = y1 - y2;
  120. if (dx == 0.0 && dy == 0.0)
  121. return G_distance(x1, y1, xp, yp);
  122. if (fabs(dy) > fabs(dx)) {
  123. xq = xp + dy;
  124. yq = (dx / dy) * (xp - xq) + yp;
  125. }
  126. else {
  127. yq = yp + dx;
  128. xq = (dy / dx) * (yp - yq) + xp;
  129. }
  130. /* find the intersection of the perpendicular with the segment */
  131. switch (t =
  132. G_intersect_line_segments(xp, yp, xq, yq, x1, y1, x2, y2, &ra,
  133. &rb, &x, &y)) {
  134. case 0:
  135. case 1:
  136. break;
  137. default:
  138. /* parallel/colinear cases shouldn't occur with perpendicular lines */
  139. G_warning(_("G_distance_point_to_line_segment: shouldn't happen: "
  140. "code=%d P=(%f,%f) S=(%f,%f)(%f,%f)"),
  141. t, xp, yp, x1, y1, x2, y2);
  142. return -1.0;
  143. }
  144. /* if x,y lies on the segment, then the distance is from to x,y */
  145. if (rb >= 0 && rb <= 1.0)
  146. return G_distance(x, y, xp, yp);
  147. /* otherwise the distance is the short of the distances to the endpoints
  148. * of the segment
  149. */
  150. return min2(G_distance(x1, y1, xp, yp), G_distance(x2, y2, xp, yp));
  151. }
  152. static double min4(double a, double b, double c, double d)
  153. {
  154. return min2(min2(a, b), min2(c, d));
  155. }
  156. static double min2(double a, double b)
  157. {
  158. return a < b ? a : b;
  159. }