From b465dabe77bb9ebe67bca5838529dda4258e835d Mon Sep 17 00:00:00 2001 From: Daniel Karch Date: Tue, 11 Mar 2014 16:40:20 +0100 Subject: [PATCH 1/3] Refactored the EdgeBasedNode class. This includes more robust computations in ComputePerpendicularDistance. There were cases where ComputePerpendicularDistance divided by zero and had to handle special cases, even though this was not necessary. --- DataStructures/EdgeBasedNode.h | 226 +++++++++++++++++++++------------ 1 file changed, 144 insertions(+), 82 deletions(-) diff --git a/DataStructures/EdgeBasedNode.h b/DataStructures/EdgeBasedNode.h index e963af026a2..b621b049fc6 100644 --- a/DataStructures/EdgeBasedNode.h +++ b/DataStructures/EdgeBasedNode.h @@ -9,8 +9,11 @@ #include "../Util/SimpleLogger.h" #include "../typedefs.h" +#include + #include +// An EdgeBasedNode represents a node in the edge-expanded graph. struct EdgeBasedNode { EdgeBasedNode() : @@ -25,101 +28,160 @@ struct EdgeBasedNode { ignoreInGrid(false) { } - + // Computes: + // - the distance from the given query location to nearest point on this edge (and returns it) + // - the location on this edge which is nearest to the query location + // - the ratio ps:pq, where p and q are the end points of this edge, and s is the perpendicular foot of + // the query location on the line defined by p and q. double ComputePerpendicularDistance( - const FixedPointCoordinate& query_location, - FixedPointCoordinate & nearest_location, - double & r - ) const { + const FixedPointCoordinate& query_location, + FixedPointCoordinate & nearest_location, + double & ratio, + double precision = COORDINATE_PRECISION + ) const { BOOST_ASSERT( query_location.isValid() ); + double epsilon = 1.0/COORDINATE_PRECISION; + if( ignoreInGrid ) { return std::numeric_limits::max(); } - - const double x = lat2y(query_location.lat/COORDINATE_PRECISION); - const double y = query_location.lon/COORDINATE_PRECISION; - const double a = lat2y(lat1/COORDINATE_PRECISION); - const double b = lon1/COORDINATE_PRECISION; - const double c = lat2y(lat2/COORDINATE_PRECISION); - const double d = lon2/COORDINATE_PRECISION; - double p,q/*,mX*/,nY; - if( std::abs(a-c) > std::numeric_limits::epsilon() ){ - const double m = (d-b)/(c-a); // slope - // Projection of (x,y) on line joining (a,b) and (c,d) - p = ((x + (m*y)) + (m*m*a - m*b))/(1. + m*m); - q = b + m*(p - a); - } else { - p = c; - q = y; - } - nY = (d*p - c*q)/(a*d - b*c); - - //discretize the result to coordinate precision. it's a hack! - if( std::abs(nY) < (1./COORDINATE_PRECISION) ) { - nY = 0.; - } - - r = (p - nY*a)/c;// These values are actually n/m+n and m/m+n , we need - // not calculate the explicit values of m an n as we - // are just interested in the ratio - if( std::isnan(r) ) { - r = ((lat2 == query_location.lat) && (lon2 == query_location.lon)) ? 1. : 0.; - } else if( std::abs(r) <= std::numeric_limits::epsilon() ) { - r = 0.; - } else if( std::abs(r-1.) <= std::numeric_limits::epsilon() ) { - r = 1.; - } - BOOST_ASSERT( !std::isnan(r) ); - if( r <= 0. ){ - nearest_location.lat = lat1; - nearest_location.lon = lon1; - } else if( r >= 1. ){ - nearest_location.lat = lat2; - nearest_location.lon = lon2; - } else { - // point lies in between - nearest_location.lat = y2lat(p)*COORDINATE_PRECISION; - nearest_location.lon = q*COORDINATE_PRECISION; - } - BOOST_ASSERT( nearest_location.isValid() ); - - // TODO: Replace with euclidean approximation when k-NN search is done - // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( - const double approximated_distance = FixedPointCoordinate::ApproximateDistance( - query_location, - nearest_location - ); - BOOST_ASSERT( 0. <= approximated_distance ); - return approximated_distance; + + // p, q : the end points of the underlying edge + const Point p(lat2y(lat1/COORDINATE_PRECISION), lon1/COORDINATE_PRECISION); + const Point q(lat2y(lat2/COORDINATE_PRECISION), lon2/COORDINATE_PRECISION); + + // r : query location + const Point r(lat2y(query_location.lat/COORDINATE_PRECISION), + query_location.lon/COORDINATE_PRECISION); + + const Point foot = ComputePerpendicularFoot(p, q, r, epsilon); + ratio = ComputeRatio(p, q, foot, epsilon); + + BOOST_ASSERT( !std::isnan(ratio) ); + + nearest_location = ComputeNearestPointOnSegment(foot, ratio); + + BOOST_ASSERT( nearest_location.isValid() ); + + // TODO: Replace with euclidean approximation when k-NN search is done + // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( + const double approximated_distance = + FixedPointCoordinate::ApproximateDistance(query_location, nearest_location); + + BOOST_ASSERT( 0. <= approximated_distance ); + return approximated_distance; + } + + bool operator<(const EdgeBasedNode & other) const { + return other.id < id; + } + + bool operator==(const EdgeBasedNode & other) const { + return id == other.id; + } + + // Returns the midpoint of the underlying edge. + inline FixedPointCoordinate Centroid() const { + return FixedPointCoordinate((lat1+lat2)/2, (lon1+lon2)/2); + } + + NodeID id; + + // The coordinates of the end-points of the underlying edge. + int lat1; + int lat2; + int lon1; + int lon2:31; + + bool belongsToTinyComponent:1; + NodeID nameID; + + // The weight of the underlying edge. + unsigned weight:31; + + bool ignoreInGrid:1; + +private: + + struct Point + { + const double x; + const double y; + + Point(double x, double y) + : x(x), y(y) {} + }; + + // Compute the perpendicular foot of point r on the line defined by p and q. + Point ComputePerpendicularFoot(const Point &p, const Point &q, const Point &r, double epsilon) const { + + // the projection of r onto the line pq + double foot_x; + double foot_y; + + const bool parallel_to_y_axis = std::abs(q.x - p.x) < epsilon; + + if( parallel_to_y_axis ) { + foot_x = q.x; + foot_y = r.y; + } else { + // the slope of the line through (a|b) and (c|d) + const double m = (q.y - p.y) / (q.x - p.x); + + // Projection of (x|y) onto the line joining (a|b) and (c|d). + foot_x = ((r.x + (m*r.y)) + (m*m*p.x - m*p.y))/(1.0 + m*m); + foot_y = p.y + m*(foot_x - p.x); + } + + return Point(foot_x, foot_y); } - bool operator<(const EdgeBasedNode & other) const { - return other.id < id; + // Compute the ratio of the line segment pr to line segment pq. + double ComputeRatio(const Point & p, const Point & q, const Point & r, double epsilon) const { + + const bool parallel_to_x_axis = std::abs(q.y-p.y) < epsilon; + const bool parallel_to_y_axis = std::abs(q.x-p.x) < epsilon; + + double ratio; + + if( !parallel_to_y_axis ) { + ratio = (r.x - p.x)/(q.x - p.x); + } else if( !parallel_to_x_axis ) { + ratio = (r.y - p.y)/(q.y - p.y); + } else { + // (a|b) and (c|d) are essentially the same point + // by convention, we set the ratio to 0 in this case + //ratio = ((lat2 == query_location.lat) && (lon2 == query_location.lon)) ? 1. : 0.; + ratio = 0.0; + } + + // Round to integer if the ratio is close to 0 or 1. + if( std::abs(ratio) <= epsilon ) { + ratio = 0.0; + } else if( std::abs(ratio-1.0) <= epsilon ) { + ratio = 1.0; + } + + return ratio; } - bool operator==(const EdgeBasedNode & other) const { - return id == other.id; - } + // Computes the point on the segment pq which is nearest to a point r = p + lambda * (q-p). + // p and q are the end points of the underlying edge. + FixedPointCoordinate ComputeNearestPointOnSegment(const Point & r, double lambda) const { + + if( lambda <= 0.0 ) { + return FixedPointCoordinate(lat1, lon1); + } else if( lambda >= 1.0 ) { + return FixedPointCoordinate(lat2, lon2); + } else { + // r lies between p and q + return FixedPointCoordinate(y2lat(r.x)*COORDINATE_PRECISION, + r.y*COORDINATE_PRECISION); + } - inline FixedPointCoordinate Centroid() const { - FixedPointCoordinate centroid; - //The coordinates of the midpoint are given by: - //x = (x1 + x2) /2 and y = (y1 + y2) /2. - centroid.lon = (std::min(lon1, lon2) + std::max(lon1, lon2))/2; - centroid.lat = (std::min(lat1, lat2) + std::max(lat1, lat2))/2; - return centroid; } - NodeID id; - int lat1; - int lat2; - int lon1; - int lon2:31; - bool belongsToTinyComponent:1; - NodeID nameID; - unsigned weight:31; - bool ignoreInGrid:1; }; #endif //EDGE_BASED_NODE_H From f923f508f5df41212eb1f437c35ff478193bbb2b Mon Sep 17 00:00:00 2001 From: Daniel Karch Date: Wed, 12 Mar 2014 10:24:35 +0100 Subject: [PATCH 2/3] applied requested changes to EdgeBasedNode.h --- DataStructures/EdgeBasedNode.h | 252 ++++++++++++++++----------------- 1 file changed, 121 insertions(+), 131 deletions(-) diff --git a/DataStructures/EdgeBasedNode.h b/DataStructures/EdgeBasedNode.h index b621b049fc6..c93241b20c2 100644 --- a/DataStructures/EdgeBasedNode.h +++ b/DataStructures/EdgeBasedNode.h @@ -9,176 +9,166 @@ #include "../Util/SimpleLogger.h" #include "../typedefs.h" -#include - #include -// An EdgeBasedNode represents a node in the edge-expanded graph. +// An EdgeBasedNode represents a node in the edge-expanded graph. struct EdgeBasedNode { EdgeBasedNode() : - id(INT_MAX), - lat1(INT_MAX), - lat2(INT_MAX), - lon1(INT_MAX), - lon2(INT_MAX >> 1), - belongsToTinyComponent(false), - nameID(UINT_MAX), - weight(UINT_MAX >> 1), - ignoreInGrid(false) + id(INT_MAX), + lat1(INT_MAX), + lat2(INT_MAX), + lon1(INT_MAX), + lon2(INT_MAX >> 1), + belongsToTinyComponent(false), + nameID(UINT_MAX), + weight(UINT_MAX >> 1), + ignoreInGrid(false) { } - // Computes: - // - the distance from the given query location to nearest point on this edge (and returns it) - // - the location on this edge which is nearest to the query location - // - the ratio ps:pq, where p and q are the end points of this edge, and s is the perpendicular foot of - // the query location on the line defined by p and q. + // Computes: + // - the distance from the given query location to nearest point on this edge (and returns it) + // - the location on this edge which is nearest to the query location + // - the ratio ps:pq, where p and q are the end points of this edge, and s is the perpendicular foot of + // the query location on the line defined by p and q. double ComputePerpendicularDistance( - const FixedPointCoordinate& query_location, - FixedPointCoordinate & nearest_location, - double & ratio, - double precision = COORDINATE_PRECISION - ) const { + const FixedPointCoordinate& query_location, + FixedPointCoordinate & nearest_location, + double & ratio, + double precision = COORDINATE_PRECISION + ) const { BOOST_ASSERT( query_location.isValid() ); - double epsilon = 1.0/COORDINATE_PRECISION; + const double epsilon = 1.0/COORDINATE_PRECISION; if( ignoreInGrid ) { return std::numeric_limits::max(); } - - // p, q : the end points of the underlying edge + + // p, q : the end points of the underlying edge const Point p(lat2y(lat1/COORDINATE_PRECISION), lon1/COORDINATE_PRECISION); const Point q(lat2y(lat2/COORDINATE_PRECISION), lon2/COORDINATE_PRECISION); - // r : query location + // r : query location const Point r(lat2y(query_location.lat/COORDINATE_PRECISION), - query_location.lon/COORDINATE_PRECISION); + query_location.lon/COORDINATE_PRECISION); const Point foot = ComputePerpendicularFoot(p, q, r, epsilon); - ratio = ComputeRatio(p, q, foot, epsilon); - - BOOST_ASSERT( !std::isnan(ratio) ); - - nearest_location = ComputeNearestPointOnSegment(foot, ratio); - - BOOST_ASSERT( nearest_location.isValid() ); - - // TODO: Replace with euclidean approximation when k-NN search is done - // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( - const double approximated_distance = - FixedPointCoordinate::ApproximateDistance(query_location, nearest_location); - - BOOST_ASSERT( 0. <= approximated_distance ); - return approximated_distance; - } - - bool operator<(const EdgeBasedNode & other) const { - return other.id < id; - } - - bool operator==(const EdgeBasedNode & other) const { - return id == other.id; - } - - // Returns the midpoint of the underlying edge. - inline FixedPointCoordinate Centroid() const { - return FixedPointCoordinate((lat1+lat2)/2, (lon1+lon2)/2); - } - - NodeID id; - - // The coordinates of the end-points of the underlying edge. - int lat1; - int lat2; - int lon1; - int lon2:31; - - bool belongsToTinyComponent:1; - NodeID nameID; - - // The weight of the underlying edge. - unsigned weight:31; - - bool ignoreInGrid:1; + ratio = ComputeRatio(p, q, foot, epsilon); + + BOOST_ASSERT( !std::isnan(ratio) ); + + nearest_location = ComputeNearestPointOnSegment(foot, ratio); + + BOOST_ASSERT( nearest_location.isValid() ); + + // TODO: Replace with euclidean approximation when k-NN search is done + // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( + const double approximated_distance = FixedPointCoordinate::ApproximateDistance(query_location, nearest_location); + + BOOST_ASSERT( 0.0 <= approximated_distance ); + return approximated_distance; + } + + bool operator<(const EdgeBasedNode & other) const { + return other.id < id; + } + + bool operator==(const EdgeBasedNode & other) const { + return id == other.id; + } + + // Returns the midpoint of the underlying edge. + inline FixedPointCoordinate Centroid() const { + return FixedPointCoordinate((lat1+lat2)/2, (lon1+lon2)/2); + } + + NodeID id; + + // The coordinates of the end-points of the underlying edge. + int lat1; + int lat2; + int lon1; + int lon2:31; + + bool belongsToTinyComponent:1; + NodeID nameID; + + // The weight of the underlying edge. + unsigned weight:31; + + bool ignoreInGrid:1; private: - struct Point - { - const double x; - const double y; + typedef std::pair Point; - Point(double x, double y) - : x(x), y(y) {} - }; - // Compute the perpendicular foot of point r on the line defined by p and q. + // Compute the perpendicular foot of point r on the line defined by p and q. Point ComputePerpendicularFoot(const Point &p, const Point &q, const Point &r, double epsilon) const { - // the projection of r onto the line pq - double foot_x; - double foot_y; - - const bool parallel_to_y_axis = std::abs(q.x - p.x) < epsilon; - - if( parallel_to_y_axis ) { - foot_x = q.x; - foot_y = r.y; - } else { - // the slope of the line through (a|b) and (c|d) - const double m = (q.y - p.y) / (q.x - p.x); - - // Projection of (x|y) onto the line joining (a|b) and (c|d). - foot_x = ((r.x + (m*r.y)) + (m*m*p.x - m*p.y))/(1.0 + m*m); - foot_y = p.y + m*(foot_x - p.x); - } - - return Point(foot_x, foot_y); + // the projection of r onto the line pq + double foot_x, foot_y; + + const bool is_parallel_to_y_axis = std::abs(q.first - p.first) < epsilon; + + if( is_parallel_to_y_axis ) { + foot_x = q.first; + foot_y = r.second; + } else { + // the slope of the line through (a|b) and (c|d) + const double m = (q.second - p.second) / (q.first - p.first); + + // Projection of (x|y) onto the line joining (a|b) and (c|d). + foot_x = ((r.first + (m*r.second)) + (m*m*p.first - m*p.second))/(1.0 + m*m); + foot_y = p.second + m*(foot_x - p.first); + } + + return Point(foot_x, foot_y); } // Compute the ratio of the line segment pr to line segment pq. double ComputeRatio(const Point & p, const Point & q, const Point & r, double epsilon) const { - const bool parallel_to_x_axis = std::abs(q.y-p.y) < epsilon; - const bool parallel_to_y_axis = std::abs(q.x-p.x) < epsilon; - + const bool is_parallel_to_x_axis = std::abs(q.second-p.second) < epsilon; + const bool is_parallel_to_y_axis = std::abs(q.first-p.first) < epsilon; + double ratio; - if( !parallel_to_y_axis ) { - ratio = (r.x - p.x)/(q.x - p.x); - } else if( !parallel_to_x_axis ) { - ratio = (r.y - p.y)/(q.y - p.y); - } else { - // (a|b) and (c|d) are essentially the same point - // by convention, we set the ratio to 0 in this case - //ratio = ((lat2 == query_location.lat) && (lon2 == query_location.lon)) ? 1. : 0.; - ratio = 0.0; - } - - // Round to integer if the ratio is close to 0 or 1. - if( std::abs(ratio) <= epsilon ) { - ratio = 0.0; - } else if( std::abs(ratio-1.0) <= epsilon ) { - ratio = 1.0; - } - - return ratio; + if( !is_parallel_to_y_axis ) { + ratio = (r.first - p.first)/(q.first - p.first); + } else if( !is_parallel_to_x_axis ) { + ratio = (r.second - p.second)/(q.second - p.second); + } else { + // (a|b) and (c|d) are essentially the same point + // by convention, we set the ratio to 0 in this case + //ratio = ((lat2 == query_location.lat) && (lon2 == query_location.lon)) ? 1. : 0.; + ratio = 0.0; + } + + // Round to integer if the ratio is close to 0 or 1. + if( std::abs(ratio) <= epsilon ) { + ratio = 0.0; + } else if( std::abs(ratio-1.0) <= epsilon ) { + ratio = 1.0; + } + + return ratio; } // Computes the point on the segment pq which is nearest to a point r = p + lambda * (q-p). - // p and q are the end points of the underlying edge. + // p and q are the end points of the underlying edge. FixedPointCoordinate ComputeNearestPointOnSegment(const Point & r, double lambda) const { - if( lambda <= 0.0 ) { - return FixedPointCoordinate(lat1, lon1); - } else if( lambda >= 1.0 ) { - return FixedPointCoordinate(lat2, lon2); - } else { - // r lies between p and q - return FixedPointCoordinate(y2lat(r.x)*COORDINATE_PRECISION, - r.y*COORDINATE_PRECISION); - } + if( lambda <= 0.0 ) { + return FixedPointCoordinate(lat1, lon1); + } else if( lambda >= 1.0 ) { + return FixedPointCoordinate(lat2, lon2); + } else { + // r lies between p and q + return FixedPointCoordinate(y2lat(r.first)*COORDINATE_PRECISION, + r.second*COORDINATE_PRECISION); + } } From ecfda146b25bbe5d1e73826ed67b6e3ea1815a37 Mon Sep 17 00:00:00 2001 From: Daniel Karch Date: Wed, 12 Mar 2014 10:48:54 +0100 Subject: [PATCH 3/3] minor change --- DataStructures/EdgeBasedNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataStructures/EdgeBasedNode.h b/DataStructures/EdgeBasedNode.h index c93241b20c2..82b774b9de8 100644 --- a/DataStructures/EdgeBasedNode.h +++ b/DataStructures/EdgeBasedNode.h @@ -39,7 +39,7 @@ struct EdgeBasedNode { ) const { BOOST_ASSERT( query_location.isValid() ); - const double epsilon = 1.0/COORDINATE_PRECISION; + const double epsilon = 1.0/precision; if( ignoreInGrid ) { return std::numeric_limits::max();