Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored the EdgeBasedNode class. #946

Merged
merged 3 commits into from
Mar 12, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 118 additions & 66 deletions DataStructures/EdgeBasedNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,86 +11,62 @@

#include <osrm/Coordinate.h>

// 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.
double ComputePerpendicularDistance(
const FixedPointCoordinate& query_location,
FixedPointCoordinate & nearest_location,
double & r
) const {
double & ratio,
double precision = COORDINATE_PRECISION
) const {
BOOST_ASSERT( query_location.isValid() );

const double epsilon = 1.0/precision;

if( ignoreInGrid ) {
return std::numeric_limits<double>::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<double>::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);
// 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);

//discretize the result to coordinate precision. it's a hack!
if( std::abs(nY) < (1./COORDINATE_PRECISION) ) {
nY = 0.;
}
// 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);

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<double>::epsilon() ) {
r = 0.;
} else if( std::abs(r-1.) <= std::numeric_limits<double>::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 );
const double approximated_distance = FixedPointCoordinate::ApproximateDistance(query_location, nearest_location);

BOOST_ASSERT( 0.0 <= approximated_distance );
return approximated_distance;
}

Expand All @@ -102,24 +78,100 @@ struct EdgeBasedNode {
return id == other.id;
}

// Returns the midpoint of the underlying edge.
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;
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:

typedef std::pair<double,double> Point;


// 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, 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 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( !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.
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.first)*COORDINATE_PRECISION,
r.second*COORDINATE_PRECISION);
}

}

};

#endif //EDGE_BASED_NODE_H