distance.c 4.5 KB

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