distance.c 4.6 KB

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