distance.c 4.5 KB

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