/*! \file lib/gis/distance.c \brief GIS Library - Distance calculation functions. WARNING: this code is preliminary and may be changed, including calling sequences to any of the functions defined here. (C) 2001-2009, 2011 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. \author Original author CERL */ #include #include #include static double min4(double, double, double, double); static double min2(double, double); static struct state { int projection; double factor; } state; static struct state *st = &state; /*! \brief Begin distance calculations. Initializes the distance calculations. It is used both for the planimetric and latitude-longitude projections. \return 0 if projection has no metrix (ie. imagery) \return 1 if projection is planimetric \return 2 if projection is latitude-longitude */ int G_begin_distance_calculations(void) { double a, e2; st->factor = 1.0; switch (st->projection = G_projection()) { case PROJECTION_LL: G_get_ellipsoid_parameters(&a, &e2); G_begin_geodesic_distance(a, e2); return 2; default: st->factor = G_database_units_to_meters_factor(); if (st->factor <= 0.0) { st->factor = 1.0; /* assume meter grid */ return 0; } return 1; } } /*! \brief Returns distance in meters. This routine computes the distance, in meters, from x1,y1 to x2,y2. If the projection is latitude-longitude, this distance is measured along the geodesic. Two routines perform geodesic distance calculations. \param e1,n1 east-north coordinates of first point \param e2,n2 east-north coordinates of second point \return distance */ double G_distance(double e1, double n1, double e2, double n2) { if (st->projection == PROJECTION_LL) return G_geodesic_distance(e1, n1, e2, n2); else return st->factor * hypot(e1 - e2, n1 - n2); } /*! \brief Returns distance between two line segments in meters. \param ax1,ay1,ax2,ay2 first segment \param bx1,by1,bx2,by2 second segment \return distance value */ double G_distance_between_line_segments(double ax1, double ay1, double ax2, double ay2, double bx1, double by1, double bx2, double by2) { double ra, rb; double x, y; /* if the segments intersect, then the distance is zero */ if (G_intersect_line_segments(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, &ra, &rb, &x, &y) > 0) return 0.0; return min4(G_distance_point_to_line_segment(ax1, ay1, bx1, by1, bx2, by2), G_distance_point_to_line_segment(ax2, ay2, bx1, by1, bx2, by2), G_distance_point_to_line_segment(bx1, by1, ax1, ay1, ax2, ay2), G_distance_point_to_line_segment(bx2, by2, ax1, ay1, ax2, ay2) ); } /*! \brief Returns distance between a point and line segment in meters. \param xp,yp point coordinates \param x1,y1 segment point coordinates \param x2,y2 segment point coordinates \return distance */ double G_distance_point_to_line_segment(double xp, double yp, double x1, double y1, double x2, double y2) { double dx, dy; double x, y; double xq, yq, ra, rb; int t; /* define the perpendicular to the segment thru the point */ dx = x1 - x2; dy = y1 - y2; if (dx == 0.0 && dy == 0.0) return G_distance(x1, y1, xp, yp); if (fabs(dy) > fabs(dx)) { xq = xp + dy; yq = (dx / dy) * (xp - xq) + yp; } else { yq = yp + dx; xq = (dy / dx) * (yp - yq) + xp; } /* find the intersection of the perpendicular with the segment */ t = G_intersect_line_segments(xp, yp, xq, yq, x1, y1, x2, y2, &ra, &rb, &x, &y); switch (t) { case 0: case 1: break; default: /* parallel/colinear cases shouldn't occur with perpendicular lines */ G_warning(_("%s: shouldn't happen: " "code=%d P=(%f,%f) S=(%f,%f)(%f,%f)"), "G_distance_point_to_line_segment", t, xp, yp, x1, y1, x2, y2); return -1.0; } /* if x,y lies on the segment, then the distance is from to x,y */ if (rb >= 0 && rb <= 1.0) return G_distance(x, y, xp, yp); /* otherwise the distance is the short of the distances to the endpoints * of the segment */ return min2(G_distance(x1, y1, xp, yp), G_distance(x2, y2, xp, yp)); } static double min4(double a, double b, double c, double d) { return min2(min2(a, b), min2(c, d)); } static double min2(double a, double b) { return a < b ? a : b; }