From 8983c0f92718cae19232037edfa01fd5dd3a83c7 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 13:06:00 +0200 Subject: [PATCH] move GetBearing(.) function into FixedPointCoordinate --- DataStructures/Coordinate.cpp | 893 +++++++++++++++-------------- Descriptors/DescriptionFactory.cpp | 33 -- Descriptors/DescriptionFactory.h | 4 +- Descriptors/JSONDescriptor.h | 713 ++++++++++++----------- Include/osrm/Coordinate.h | 7 + 5 files changed, 837 insertions(+), 813 deletions(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 8383b6abc..05ab8c204 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -1,420 +1,473 @@ -/* - -Copyright (c) 2013, Project OSRM, Dennis Luxen, others -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include "../Util/MercatorUtil.h" -#include "../Util/SimpleLogger.h" -#include "../Util/StringUtil.h" - -#include - -#ifndef NDEBUG -#include -#endif -#include -#include - -FixedPointCoordinate::FixedPointCoordinate() - : lat(std::numeric_limits::min()), lon(std::numeric_limits::min()) -{ -} - -FixedPointCoordinate::FixedPointCoordinate(int lat, int lon) : lat(lat), lon(lon) -{ -#ifndef NDEBUG - if (0 != (std::abs(lat) >> 30)) - { - std::bitset<32> y(lat); - SimpleLogger().Write(logDEBUG) << "broken lat: " << lat << ", bits: " << y; - } - if (0 != (std::abs(lon) >> 30)) - { - std::bitset<32> x(lon); - SimpleLogger().Write(logDEBUG) << "broken lon: " << lon << ", bits: " << x; - } -#endif -} - -void FixedPointCoordinate::Reset() -{ - lat = std::numeric_limits::min(); - lon = std::numeric_limits::min(); -} -bool FixedPointCoordinate::isSet() const -{ - return (std::numeric_limits::min() != lat) && (std::numeric_limits::min() != lon); -} -bool FixedPointCoordinate::isValid() const -{ - if (lat > 90 * COORDINATE_PRECISION || lat < -90 * COORDINATE_PRECISION || - lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION) - { - return false; - } - return true; -} -bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const -{ - return lat == other.lat && lon == other.lon; -} - -double FixedPointCoordinate::ApproximateDistance(const int lat1, - const int lon1, - const int lat2, - const int lon2) -{ - BOOST_ASSERT(lat1 != std::numeric_limits::min()); - BOOST_ASSERT(lon1 != std::numeric_limits::min()); - BOOST_ASSERT(lat2 != std::numeric_limits::min()); - BOOST_ASSERT(lon2 != std::numeric_limits::min()); - double RAD = 0.017453292519943295769236907684886; - double lt1 = lat1 / COORDINATE_PRECISION; - double ln1 = lon1 / COORDINATE_PRECISION; - double lt2 = lat2 / COORDINATE_PRECISION; - double ln2 = lon2 / COORDINATE_PRECISION; - double dlat1 = lt1 * (RAD); - - double dlong1 = ln1 * (RAD); - double dlat2 = lt2 * (RAD); - double dlong2 = ln2 * (RAD); - - double dLong = dlong1 - dlong2; - double dLat = dlat1 - dlat2; - - double aHarv = pow(sin(dLat / 2.0), 2.0) + cos(dlat1) * cos(dlat2) * pow(sin(dLong / 2.), 2); - double cHarv = 2. * atan2(sqrt(aHarv), sqrt(1.0 - aHarv)); - // earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi) - // The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) - const double earth = 6372797.560856; - return earth * cHarv; -} - -double FixedPointCoordinate::ApproximateDistance(const FixedPointCoordinate &c1, - const FixedPointCoordinate &c2) -{ - return ApproximateDistance(c1.lat, c1.lon, c2.lat, c2.lon); -} - -double FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1, - const FixedPointCoordinate &c2) -{ - return ApproximateEuclideanDistance(c1.lat, c1.lon, c2.lat, c2.lon); -} - -double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, - const int lon1, - const int lat2, - const int lon2) -{ - BOOST_ASSERT(lat1 != std::numeric_limits::min()); - BOOST_ASSERT(lon1 != std::numeric_limits::min()); - BOOST_ASSERT(lat2 != std::numeric_limits::min()); - BOOST_ASSERT(lon2 != std::numeric_limits::min()); - - const double RAD = 0.017453292519943295769236907684886; - const double float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; - const double float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; - const double float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; - const double float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; - - const double x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.); - const double y = (float_lat2 - float_lat1); - const double earth_radius = 6372797.560856; - return sqrt(x * x + y * y) * earth_radius; -} - -// Yuck! Code duplication. This function is also in EgdeBasedNode.h -double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, - const FixedPointCoordinate &segA, - const FixedPointCoordinate &segB) -{ - const double x = lat2y(point.lat / COORDINATE_PRECISION); - const double y = point.lon / COORDINATE_PRECISION; - const double a = lat2y(segA.lat / COORDINATE_PRECISION); - const double b = segA.lon / COORDINATE_PRECISION; - const double c = lat2y(segB.lat / COORDINATE_PRECISION); - const double d = segB.lon / COORDINATE_PRECISION; - double p, q, 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.; - } - - double r = (p - nY * a) / c; - if (std::isnan(r)) - { - r = ((segB.lat == point.lat) && (segB.lon == point.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.; - } - FixedPointCoordinate nearest_location; - BOOST_ASSERT(!std::isnan(r)); - if (r <= 0.) - { // point is "left" of edge - nearest_location.lat = segA.lat; - nearest_location.lon = segA.lon; - } - else if (r >= 1.) - { // point is "right" of edge - nearest_location.lat = segB.lat; - nearest_location.lon = segB.lon; - } - else - { // point lies in between - nearest_location.lat = y2lat(p) * COORDINATE_PRECISION; - nearest_location.lon = q * COORDINATE_PRECISION; - } - BOOST_ASSERT(nearest_location.isValid()); - const double approximated_distance = - FixedPointCoordinate::ApproximateDistance(point, nearest_location); - BOOST_ASSERT(0. <= approximated_distance); - return approximated_distance; -} - -double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, - const FixedPointCoordinate &coord_b, - const FixedPointCoordinate &query_location, - FixedPointCoordinate &nearest_location, - double &r) -{ - BOOST_ASSERT(query_location.isValid()); - - const double x = lat2y(query_location.lat / COORDINATE_PRECISION); - const double y = query_location.lon / COORDINATE_PRECISION; - const double a = lat2y(coord_a.lat / COORDINATE_PRECISION); - const double b = coord_a.lon / COORDINATE_PRECISION; - const double c = lat2y(coord_b.lat / COORDINATE_PRECISION); - const double d = coord_b.lon / 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 = ((coord_b.lat == query_location.lat) && (coord_b.lon == 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 = coord_a.lat; - nearest_location.lon = coord_a.lon; - } - else if (r >= 1.) - { - nearest_location.lat = coord_b.lat; - nearest_location.lon = coord_b.lon; - } - 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; -} - -void FixedPointCoordinate::convertInternalLatLonToString(const int value, std::string &output) -{ - char buffer[12]; - buffer[11] = 0; // zero termination - output = printInt<11, 6>(buffer, value); -} - -void FixedPointCoordinate::convertInternalCoordinateToString(const FixedPointCoordinate &coord, - std::string &output) -{ - std::string tmp; - tmp.reserve(23); - convertInternalLatLonToString(coord.lon, tmp); - output = tmp; - output += ","; - convertInternalLatLonToString(coord.lat, tmp); - output += tmp; -} - -void -FixedPointCoordinate::convertInternalReversedCoordinateToString(const FixedPointCoordinate &coord, - std::string &output) -{ - std::string tmp; - tmp.reserve(23); - convertInternalLatLonToString(coord.lat, tmp); - output = tmp; - output += ","; - convertInternalLatLonToString(coord.lon, tmp); - output += tmp; -} - -void FixedPointCoordinate::Output(std::ostream &out) const -{ - out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; -} - - -// double PointSegmentDistanceSquared( double px, double py, -// double p1x, double p1y, -// double p2x, double p2y, -// double& t, -// double& qx, double& qy) -// { -// static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f -// static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f -// double dx = p2x - p1x; -// double dy = p2y - p1y; -// double dp1x = px - p1x; -// double dp1y = py - p1y; -// const double segLenSquared = (dx * dx) + (dy * dy); -// if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared) -// { -// // segment is a point. -// qx = p1x; -// qy = p1y; -// t = 0.0; -// return ((dp1x * dp1x) + (dp1y * dp1y)); -// } -// else -// { -// // Project a line from p to the segment [p1,p2]. By considering the line -// // extending the segment, parameterized as p1 + (t * (p2 - p1)), -// // we find projection of point p onto the line. -// // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2 -// t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared; -// if (t < kEpsilon) -// { -// // intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then -// // intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within -// // the 'bounds' of the segment) -// if (t > -kEpsilon) -// { -// // intersects at 1st segment vertex -// t = 0.0; -// } -// // set our 'intersection' point to p1. -// qx = p1x; -// qy = p1y; -// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if -// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). -// } -// else if (t > (1.0 - kEpsilon)) -// { -// // intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then -// // intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within -// // the 'bounds' of the segment) -// if (t < (1.0 + kEpsilon)) -// { -// // intersects at 2nd segment vertex -// t = 1.0; -// } -// // set our 'intersection' point to p2. -// qx = p2x; -// qy = p2y; -// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if -// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). -// } -// else -// { -// // The projection of the point to the point on the segment that is perpendicular succeeded and the point -// // is 'within' the bounds of the segment. Set the intersection point as that projected point. -// qx = p1x + (t * dx); -// qy = p1y + (t * dy); -// } -// // return the squared distance from p to the intersection point. Note that we return the squared distance -// // as an optimization because many times you just need to compare relative distances and the squared values -// // works fine for that. If you want the ACTUAL distance, just take the square root of this value. -// double dpqx = px - qx; -// double dpqy = py - qy; -// return ((dpqx * dpqx) + (dpqy * dpqy)); -// } -// } - -// public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p) -// { -// // (y1-y2)x + (x2-x1)y + (x1y2-x2y1) -// //d(P,L) = -------------------------------- -// // sqrt( (x2-x1)pow2 + (y2-y1)pow2 ) - -// double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y); -// double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); -// double d = ch / del; -// return (float)d; -// } +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "../Util/MercatorUtil.h" +#include "../Util/SimpleLogger.h" +#include "../Util/StringUtil.h" + +#include + +#ifndef NDEBUG +#include +#endif +#include +#include + +FixedPointCoordinate::FixedPointCoordinate() + : lat(std::numeric_limits::min()), lon(std::numeric_limits::min()) +{ +} + +FixedPointCoordinate::FixedPointCoordinate(int lat, int lon) : lat(lat), lon(lon) +{ +#ifndef NDEBUG + if (0 != (std::abs(lat) >> 30)) + { + std::bitset<32> y(lat); + SimpleLogger().Write(logDEBUG) << "broken lat: " << lat << ", bits: " << y; + } + if (0 != (std::abs(lon) >> 30)) + { + std::bitset<32> x(lon); + SimpleLogger().Write(logDEBUG) << "broken lon: " << lon << ", bits: " << x; + } +#endif +} + +void FixedPointCoordinate::Reset() +{ + lat = std::numeric_limits::min(); + lon = std::numeric_limits::min(); +} +bool FixedPointCoordinate::isSet() const +{ + return (std::numeric_limits::min() != lat) && (std::numeric_limits::min() != lon); +} +bool FixedPointCoordinate::isValid() const +{ + if (lat > 90 * COORDINATE_PRECISION || lat < -90 * COORDINATE_PRECISION || + lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION) + { + return false; + } + return true; +} +bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const +{ + return lat == other.lat && lon == other.lon; +} + +double FixedPointCoordinate::ApproximateDistance(const int lat1, + const int lon1, + const int lat2, + const int lon2) +{ + BOOST_ASSERT(lat1 != std::numeric_limits::min()); + BOOST_ASSERT(lon1 != std::numeric_limits::min()); + BOOST_ASSERT(lat2 != std::numeric_limits::min()); + BOOST_ASSERT(lon2 != std::numeric_limits::min()); + double RAD = 0.017453292519943295769236907684886; + double lt1 = lat1 / COORDINATE_PRECISION; + double ln1 = lon1 / COORDINATE_PRECISION; + double lt2 = lat2 / COORDINATE_PRECISION; + double ln2 = lon2 / COORDINATE_PRECISION; + double dlat1 = lt1 * (RAD); + + double dlong1 = ln1 * (RAD); + double dlat2 = lt2 * (RAD); + double dlong2 = ln2 * (RAD); + + double dLong = dlong1 - dlong2; + double dLat = dlat1 - dlat2; + + double aHarv = pow(sin(dLat / 2.0), 2.0) + cos(dlat1) * cos(dlat2) * pow(sin(dLong / 2.), 2); + double cHarv = 2. * atan2(sqrt(aHarv), sqrt(1.0 - aHarv)); + // earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi) + // The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) + const double earth = 6372797.560856; + return earth * cHarv; +} + +double FixedPointCoordinate::ApproximateDistance(const FixedPointCoordinate &c1, + const FixedPointCoordinate &c2) +{ + return ApproximateDistance(c1.lat, c1.lon, c2.lat, c2.lon); +} + +double FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1, + const FixedPointCoordinate &c2) +{ + return ApproximateEuclideanDistance(c1.lat, c1.lon, c2.lat, c2.lon); +} + +double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, + const int lon1, + const int lat2, + const int lon2) +{ + BOOST_ASSERT(lat1 != std::numeric_limits::min()); + BOOST_ASSERT(lon1 != std::numeric_limits::min()); + BOOST_ASSERT(lat2 != std::numeric_limits::min()); + BOOST_ASSERT(lon2 != std::numeric_limits::min()); + + const double RAD = 0.017453292519943295769236907684886; + const double float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; + const double float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; + const double float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; + const double float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; + + const double x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.); + const double y = (float_lat2 - float_lat1); + const double earth_radius = 6372797.560856; + return sqrt(x * x + y * y) * earth_radius; +} + +// Yuck! Code duplication. This function is also in EgdeBasedNode.h +double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, + const FixedPointCoordinate &segA, + const FixedPointCoordinate &segB) +{ + const double x = lat2y(point.lat / COORDINATE_PRECISION); + const double y = point.lon / COORDINATE_PRECISION; + const double a = lat2y(segA.lat / COORDINATE_PRECISION); + const double b = segA.lon / COORDINATE_PRECISION; + const double c = lat2y(segB.lat / COORDINATE_PRECISION); + const double d = segB.lon / COORDINATE_PRECISION; + double p, q, 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.; + } + + double r = (p - nY * a) / c; + if (std::isnan(r)) + { + r = ((segB.lat == point.lat) && (segB.lon == point.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.; + } + FixedPointCoordinate nearest_location; + BOOST_ASSERT(!std::isnan(r)); + if (r <= 0.) + { // point is "left" of edge + nearest_location.lat = segA.lat; + nearest_location.lon = segA.lon; + } + else if (r >= 1.) + { // point is "right" of edge + nearest_location.lat = segB.lat; + nearest_location.lon = segB.lon; + } + else + { // point lies in between + nearest_location.lat = y2lat(p) * COORDINATE_PRECISION; + nearest_location.lon = q * COORDINATE_PRECISION; + } + BOOST_ASSERT(nearest_location.isValid()); + const double approximated_distance = + FixedPointCoordinate::ApproximateDistance(point, nearest_location); + BOOST_ASSERT(0. <= approximated_distance); + return approximated_distance; +} + +double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, + const FixedPointCoordinate &coord_b, + const FixedPointCoordinate &query_location, + FixedPointCoordinate &nearest_location, + double &r) +{ + BOOST_ASSERT(query_location.isValid()); + + const double x = lat2y(query_location.lat / COORDINATE_PRECISION); + const double y = query_location.lon / COORDINATE_PRECISION; + const double a = lat2y(coord_a.lat / COORDINATE_PRECISION); + const double b = coord_a.lon / COORDINATE_PRECISION; + const double c = lat2y(coord_b.lat / COORDINATE_PRECISION); + const double d = coord_b.lon / 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 = ((coord_b.lat == query_location.lat) && (coord_b.lon == 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 = coord_a.lat; + nearest_location.lon = coord_a.lon; + } + else if (r >= 1.) + { + nearest_location.lat = coord_b.lat; + nearest_location.lon = coord_b.lon; + } + 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; +} + +void FixedPointCoordinate::convertInternalLatLonToString(const int value, std::string &output) +{ + char buffer[12]; + buffer[11] = 0; // zero termination + output = printInt<11, 6>(buffer, value); +} + +void FixedPointCoordinate::convertInternalCoordinateToString(const FixedPointCoordinate &coord, + std::string &output) +{ + std::string tmp; + tmp.reserve(23); + convertInternalLatLonToString(coord.lon, tmp); + output = tmp; + output += ","; + convertInternalLatLonToString(coord.lat, tmp); + output += tmp; +} + +void +FixedPointCoordinate::convertInternalReversedCoordinateToString(const FixedPointCoordinate &coord, + std::string &output) +{ + std::string tmp; + tmp.reserve(23); + convertInternalLatLonToString(coord.lat, tmp); + output = tmp; + output += ","; + convertInternalLatLonToString(coord.lon, tmp); + output += tmp; +} + +void FixedPointCoordinate::Output(std::ostream &out) const +{ + out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; +} + +double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B) +{ + double delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION); + + const double lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION); + const double lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION); + + const double y = sin(delta_long) * cos(lat2); + const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); + double result = RadianToDegree(atan2(y, x)); + while (result < 0.) + { + result += 360.; + } + + while (result >= 360.) + { + result -= 360.; + } + return result; +} + +double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const +{ + double delta_long = DegreeToRadian(lon / COORDINATE_PRECISION - other.lon / COORDINATE_PRECISION); + + const double lat1 = DegreeToRadian(other.lat / COORDINATE_PRECISION); + const double lat2 = DegreeToRadian(lat / COORDINATE_PRECISION); + + const double y = sin(delta_long) * cos(lat2); + const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); + double result = RadianToDegree(atan2(y, x)); + while (result < 0.) + { + result += 360.; + } + + while (result >= 360.) + { + result -= 360.; + } + return result; +} + +double FixedPointCoordinate::DegreeToRadian(const double degree) +{ + return degree * (M_PI / 180.); +} + +double FixedPointCoordinate::RadianToDegree(const double radian) +{ + return radian * (180. / M_PI); +} + +// double PointSegmentDistanceSquared( double px, double py, +// double p1x, double p1y, +// double p2x, double p2y, +// double& t, +// double& qx, double& qy) +// { +// static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f +// static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f +// double dx = p2x - p1x; +// double dy = p2y - p1y; +// double dp1x = px - p1x; +// double dp1y = py - p1y; +// const double segLenSquared = (dx * dx) + (dy * dy); +// if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared) +// { +// // segment is a point. +// qx = p1x; +// qy = p1y; +// t = 0.0; +// return ((dp1x * dp1x) + (dp1y * dp1y)); +// } +// else +// { +// // Project a line from p to the segment [p1,p2]. By considering the line +// // extending the segment, parameterized as p1 + (t * (p2 - p1)), +// // we find projection of point p onto the line. +// // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2 +// t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared; +// if (t < kEpsilon) +// { +// // intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then +// // intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within +// // the 'bounds' of the segment) +// if (t > -kEpsilon) +// { +// // intersects at 1st segment vertex +// t = 0.0; +// } +// // set our 'intersection' point to p1. +// qx = p1x; +// qy = p1y; +// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if +// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). +// } +// else if (t > (1.0 - kEpsilon)) +// { +// // intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then +// // intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within +// // the 'bounds' of the segment) +// if (t < (1.0 + kEpsilon)) +// { +// // intersects at 2nd segment vertex +// t = 1.0; +// } +// // set our 'intersection' point to p2. +// qx = p2x; +// qy = p2y; +// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if +// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). +// } +// else +// { +// // The projection of the point to the point on the segment that is perpendicular succeeded and the point +// // is 'within' the bounds of the segment. Set the intersection point as that projected point. +// qx = p1x + (t * dx); +// qy = p1y + (t * dy); +// } +// // return the squared distance from p to the intersection point. Note that we return the squared distance +// // as an optimization because many times you just need to compare relative distances and the squared values +// // works fine for that. If you want the ACTUAL distance, just take the square root of this value. +// double dpqx = px - qx; +// double dpqy = py - qy; +// return ((dpqx * dpqx) + (dpqy * dpqy)); +// } +// } + +// public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p) +// { +// // (y1-y2)x + (x2-x1)y + (x1y2-x2y1) +// //d(P,L) = -------------------------------- +// // sqrt( (x2-x1)pow2 + (y2-y1)pow2 ) + +// double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y); +// double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); +// double d = ch / del; +// return (float)d; +// } diff --git a/Descriptors/DescriptionFactory.cpp b/Descriptors/DescriptionFactory.cpp index 7d6fc037b..0d95ddeda 100644 --- a/Descriptors/DescriptionFactory.cpp +++ b/Descriptors/DescriptionFactory.cpp @@ -31,39 +31,6 @@ DescriptionFactory::DescriptionFactory() : entireLength(0) {} DescriptionFactory::~DescriptionFactory() {} -inline double DescriptionFactory::DegreeToRadian(const double degree) const -{ - return degree * (M_PI / 180.); -} - -inline double DescriptionFactory::RadianToDegree(const double radian) const -{ - return radian * (180. / M_PI); -} - -double DescriptionFactory::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B) - const -{ - double delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION); - - const double lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION); - const double lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION); - - const double y = sin(delta_long) * cos(lat2); - const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); - double result = RadianToDegree(atan2(y, x)); - while (result < 0.) - { - result += 360.; - } - - while (result >= 360.) - { - result -= 360.; - } - return result; -} - void DescriptionFactory::SetStartSegment(const PhantomNode &source) { start_phantom = source; diff --git a/Descriptors/DescriptionFactory.h b/Descriptors/DescriptionFactory.h index 082ce675b..85c3a3c29 100644 --- a/Descriptors/DescriptionFactory.h +++ b/Descriptors/DescriptionFactory.h @@ -77,7 +77,6 @@ class DescriptionFactory std::vector path_description; DescriptionFactory(); virtual ~DescriptionFactory(); - double GetBearing(const FixedPointCoordinate &C, const FixedPointCoordinate &B) const; JSON::Value AppendUnencodedPolylineString() const; void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &data); void BuildRouteSummary(const double distance, const unsigned time); @@ -196,8 +195,7 @@ class DescriptionFactory { if (path_description[i].necessary) { - const double angle = - GetBearing(path_description[i].location, path_description[i + 1].location); + const double angle = path_description[i].location.GetBearing(path_description[i + 1].location); path_description[i].bearing = angle * 10; } } diff --git a/Descriptors/JSONDescriptor.h b/Descriptors/JSONDescriptor.h index 46b85aba2..7be9c9a73 100644 --- a/Descriptors/JSONDescriptor.h +++ b/Descriptors/JSONDescriptor.h @@ -1,357 +1,356 @@ -/* - -Copyright (c) 2013, Project OSRM, Dennis Luxen, others -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef JSON_DESCRIPTOR_H_ -#define JSON_DESCRIPTOR_H_ - -#include "BaseDescriptor.h" -#include "DescriptionFactory.h" -#include "../Algorithms/ObjectToBase64.h" -#include "../Algorithms/ExtractRouteNames.h" -#include "../DataStructures/JSONContainer.h" -#include "../DataStructures/SegmentInformation.h" -#include "../DataStructures/TurnInstructions.h" -#include "../Util/Azimuth.h" -#include "../Util/StringUtil.h" -#include "../Util/TimingUtil.h" - -#include - -template class JSONDescriptor : public BaseDescriptor -{ - private: - // TODO: initalize in c'tor - DataFacadeT *facade; - DescriptorConfig config; - DescriptionFactory description_factory, alternate_description_factory; - FixedPointCoordinate current; - unsigned entered_restricted_area_count; - struct RoundAbout - { - RoundAbout() : start_index(INT_MAX), name_id(INT_MAX), leave_at_exit(INT_MAX) {} - int start_index; - int name_id; - int leave_at_exit; - } round_about; - - struct Segment - { - Segment() : name_id(-1), length(-1), position(-1) {} - Segment(int n, int l, int p) : name_id(n), length(l), position(p) {} - int name_id; - int length; - int position; - }; - std::vector shortest_path_segments, alternative_path_segments; - std::vector shortest_leg_end_indices, alternative_leg_end_indices; - ExtractRouteNames GenerateRouteNames; - - - public: - JSONDescriptor(DataFacadeT *facade) : facade(facade), entered_restricted_area_count(0) - { - shortest_leg_end_indices.emplace_back(0); - alternative_leg_end_indices.emplace_back(0); - } - - void SetConfig(const DescriptorConfig &c) { config = c; } - - unsigned DescribeLeg(const std::vector route_leg, const PhantomNodes &leg_phantoms) - { - unsigned added_element_count = 0; - // Get all the coordinates for the computed route - FixedPointCoordinate current_coordinate; - for (const PathData &path_data : route_leg) - { - current_coordinate = facade->GetCoordinateOfNode(path_data.node); - description_factory.AppendSegment(current_coordinate, path_data); - ++added_element_count; - } - ++added_element_count; - BOOST_ASSERT((route_leg.size() + 1) == added_element_count); - return added_element_count; - } - - void Run(const RawRouteData &raw_route, - const PhantomNodes &phantom_nodes, - http::Reply &reply) - { - JSON::Object json_result; - - if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) - { - // We do not need to do much, if there is no route ;-) - json_result.values["status"] = 207; - json_result.values["status_message"] = "Cannot find route between points"; - JSON::render(reply.content, json_result); - return; - } - - // check if first segment is non-zero - std::string road_name = - facade->GetEscapedNameForNameID(phantom_nodes.source_phantom.name_id); - - BOOST_ASSERT(raw_route.unpacked_path_segments.size() == - raw_route.segment_end_coordinates.size()); - - description_factory.SetStartSegment(phantom_nodes.source_phantom); - json_result.values["status"] = 0; - json_result.values["status_message"] = "Found route between points"; - - // for each unpacked segment add the leg to the description - for (unsigned i = 0; i < raw_route.unpacked_path_segments.size(); ++i) - { - const int added_segments = DescribeLeg(raw_route.unpacked_path_segments[i], - raw_route.segment_end_coordinates[i]); - BOOST_ASSERT(0 < added_segments); - shortest_leg_end_indices.emplace_back(added_segments + shortest_leg_end_indices.back()); - } - description_factory.SetEndSegment(phantom_nodes.target_phantom); - description_factory.Run(facade, config.zoom_level); - - if (config.geometry) - { - JSON::Value route_geometry = description_factory.AppendEncodedPolylineString(config.encode_geometry); - json_result.values["route_geometry"] = route_geometry; - } - if (config.instructions) - { - JSON::Array json_route_instructions; - BuildTextualDescription(description_factory, - json_route_instructions, - raw_route.shortest_path_length, - shortest_path_segments); - json_result.values["route_instructions"] = json_route_instructions; - } - description_factory.BuildRouteSummary(description_factory.entireLength, - raw_route.shortest_path_length); - JSON::Object json_route_summary; - json_route_summary.values["total_distance"] = description_factory.summary.distance; - json_route_summary.values["total_time"] = description_factory.summary.duration; - json_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(description_factory.summary.source_name_id); - json_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(description_factory.summary.target_name_id); - json_result.values["route_summary"] = json_route_summary; - - BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); - - JSON::Array json_via_points_array; - JSON::Array json_first_coordinate; - json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lat/COORDINATE_PRECISION); - json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lon/COORDINATE_PRECISION); - json_via_points_array.values.push_back(json_first_coordinate); - for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) - { - std::string tmp; - JSON::Array json_coordinate; - json_coordinate.values.push_back(nodes.target_phantom.location.lat/COORDINATE_PRECISION); - json_coordinate.values.push_back(nodes.target_phantom.location.lon/COORDINATE_PRECISION); - json_via_points_array.values.push_back(json_coordinate); - } - json_result.values["via_points"] = json_via_points_array; - - JSON::Array json_via_indices_array; - json_via_indices_array.values.insert(json_via_indices_array.values.end(), shortest_leg_end_indices.begin(), shortest_leg_end_indices.end()); - json_result.values["via_indices"] = json_via_indices_array; - - // only one alternative route is computed at this time, so this is hardcoded - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - json_result.values["found_alternative"] = JSON::True(); - alternate_description_factory.SetStartSegment(phantom_nodes.source_phantom); - // Get all the coordinates for the computed route - for (const PathData &path_data : raw_route.unpacked_alternative) - { - current = facade->GetCoordinateOfNode(path_data.node); - alternate_description_factory.AppendSegment(current, path_data); - } - alternate_description_factory.Run(facade, config.zoom_level); - - if (config.geometry) - { - JSON::Value alternate_geometry_string = alternate_description_factory.AppendEncodedPolylineString(config.encode_geometry); - JSON::Array json_alternate_geometries_array; - json_alternate_geometries_array.values.push_back(alternate_geometry_string); - json_result.values["alternative_geometries"] = json_alternate_geometries_array; - } - // Generate instructions for each alternative (simulated here) - JSON::Array json_alt_instructions; - JSON::Array json_current_alt_instructions; - if (config.instructions) - { - BuildTextualDescription(alternate_description_factory, - json_current_alt_instructions, - raw_route.alternative_path_length, - alternative_path_segments); - json_alt_instructions.values.push_back(json_current_alt_instructions); - json_result.values["alternative_instructions"] = json_alt_instructions; - } - alternate_description_factory.BuildRouteSummary( - alternate_description_factory.entireLength, raw_route.alternative_path_length); - - JSON::Object json_alternate_route_summary; - JSON::Array json_alternate_route_summary_array; - json_alternate_route_summary.values["total_distance"] = alternate_description_factory.summary.distance; - json_alternate_route_summary.values["total_time"] = alternate_description_factory.summary.duration; - json_alternate_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.source_name_id); - json_alternate_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.target_name_id); - json_alternate_route_summary_array.values.push_back(json_alternate_route_summary); - json_result.values["alternative_summaries"] = json_alternate_route_summary_array; - - JSON::Array json_altenative_indices_array; - json_altenative_indices_array.values.push_back(0); - json_altenative_indices_array.values.push_back(alternate_description_factory.path_description.size()); - json_result.values["alternative_indices"] = json_altenative_indices_array; - } else { - json_result.values["found_alternative"] = JSON::False(); - } - - // Get Names for both routes - RouteNames route_names = GenerateRouteNames(shortest_path_segments, alternative_path_segments, facade); - JSON::Array json_route_names; - json_route_names.values.push_back(route_names.shortest_path_name_1); - json_route_names.values.push_back(route_names.shortest_path_name_2); - json_result.values["route_name"] = json_route_names; - - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - JSON::Array json_alternate_names_array; - JSON::Array json_alternate_names; - json_alternate_names.values.push_back(route_names.alternative_path_name_1); - json_alternate_names.values.push_back(route_names.alternative_path_name_2); - json_alternate_names_array.values.push_back(json_alternate_names); - json_result.values["alternative_names"] = json_alternate_names_array; - } - - JSON::Object json_hint_object; - json_hint_object.values["checksum"] = raw_route.check_sum; - JSON::Array json_location_hint_array; - std::string hint; - for (unsigned i = 0; i < raw_route.segment_end_coordinates.size(); ++i) - { - EncodeObjectToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint); - json_location_hint_array.values.push_back(hint); - } - EncodeObjectToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint); - json_location_hint_array.values.push_back(hint); - json_hint_object.values["locations"] = json_location_hint_array; - json_result.values["hint_data"] = json_hint_object; - - // render the content to the output array - TIMER_START(route_render); - JSON::render(reply.content, json_result); - TIMER_STOP(route_render); - SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render); - } - - // TODO: reorder parameters - inline void BuildTextualDescription(DescriptionFactory &description_factory, - JSON::Array & json_instruction_array, - const int route_length, - std::vector &route_segments_list) - { - // Segment information has following format: - //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] - unsigned necessary_segments_running_index = 0; - round_about.leave_at_exit = 0; - round_about.name_id = 0; - std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; - - // Fetch data from Factory and generate a string from it. - for (const SegmentInformation &segment : description_factory.path_description) - { - JSON::Array json_instruction_row; - TurnInstruction current_instruction = segment.turn_instruction; - entered_restricted_area_count += (current_instruction != segment.turn_instruction); - if (TurnInstructionsClass::TurnIsNecessary(current_instruction)) - { - if (TurnInstruction::EnterRoundAbout == current_instruction) - { - round_about.name_id = segment.name_id; - round_about.start_index = necessary_segments_running_index; - } - else - { - std::string current_turn_instruction; - if (TurnInstruction::LeaveRoundAbout == current_instruction) - { - temp_instruction = IntToString(as_integer(TurnInstruction::EnterRoundAbout)); - current_turn_instruction += temp_instruction; - current_turn_instruction += "-"; - temp_instruction = IntToString(round_about.leave_at_exit + 1); - current_turn_instruction += temp_instruction; - round_about.leave_at_exit = 0; - } - else - { - temp_instruction = IntToString(as_integer(current_instruction)); - current_turn_instruction += temp_instruction; - } - json_instruction_row.values.push_back(current_turn_instruction); - - json_instruction_row.values.push_back(facade->GetEscapedNameForNameID(segment.name_id)); - json_instruction_row.values.push_back(std::round(segment.length)); - json_instruction_row.values.push_back(necessary_segments_running_index); - json_instruction_row.values.push_back(round(segment.duration / 10)); - json_instruction_row.values.push_back(IntToString(segment.length)+"m"); - int bearing_value = round(segment.bearing / 10.); - json_instruction_row.values.push_back(Azimuth::Get(bearing_value)); - json_instruction_row.values.push_back(bearing_value); - - route_segments_list.emplace_back( - segment.name_id, segment.length, route_segments_list.size()); - json_instruction_array.values.push_back(json_instruction_row); - } - } - else if (TurnInstruction::StayOnRoundAbout == current_instruction) - { - ++round_about.leave_at_exit; - } - if (segment.necessary) - { - ++necessary_segments_running_index; - } - } - - //TODO: check if this in an invariant - if (INVALID_EDGE_WEIGHT != route_length) - { - JSON::Array json_last_instruction_row; - temp_instruction = IntToString(as_integer(TurnInstruction::ReachedYourDestination)); - json_last_instruction_row.values.push_back(temp_instruction); - json_last_instruction_row.values.push_back(""); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back(necessary_segments_running_index - 1); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back("0m"); - json_last_instruction_row.values.push_back(Azimuth::Get(0.0)); - json_last_instruction_row.values.push_back(0.); - json_instruction_array.values.push_back(json_last_instruction_row); - } - } -}; - -#endif /* JSON_DESCRIPTOR_H_ */ +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef JSON_DESCRIPTOR_H_ +#define JSON_DESCRIPTOR_H_ + +#include "BaseDescriptor.h" +#include "DescriptionFactory.h" +#include "../Algorithms/ObjectToBase64.h" +#include "../Algorithms/ExtractRouteNames.h" +#include "../DataStructures/JSONContainer.h" +#include "../DataStructures/SegmentInformation.h" +#include "../DataStructures/TurnInstructions.h" +#include "../Util/Azimuth.h" +#include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" + +#include + +template class JSONDescriptor : public BaseDescriptor +{ + private: + DataFacadeT *facade; + DescriptorConfig config; + DescriptionFactory description_factory, alternate_description_factory; + FixedPointCoordinate current; + unsigned entered_restricted_area_count; + struct RoundAbout + { + RoundAbout() : start_index(INT_MAX), name_id(INT_MAX), leave_at_exit(INT_MAX) {} + int start_index; + int name_id; + int leave_at_exit; + } round_about; + + struct Segment + { + Segment() : name_id(-1), length(-1), position(-1) {} + Segment(int n, int l, int p) : name_id(n), length(l), position(p) {} + int name_id; + int length; + int position; + }; + std::vector shortest_path_segments, alternative_path_segments; + std::vector shortest_leg_end_indices, alternative_leg_end_indices; + ExtractRouteNames GenerateRouteNames; + + + public: + JSONDescriptor(DataFacadeT *facade) : facade(facade), entered_restricted_area_count(0) + { + shortest_leg_end_indices.emplace_back(0); + alternative_leg_end_indices.emplace_back(0); + } + + void SetConfig(const DescriptorConfig &c) { config = c; } + + unsigned DescribeLeg(const std::vector route_leg, const PhantomNodes &leg_phantoms) + { + unsigned added_element_count = 0; + // Get all the coordinates for the computed route + FixedPointCoordinate current_coordinate; + for (const PathData &path_data : route_leg) + { + current_coordinate = facade->GetCoordinateOfNode(path_data.node); + description_factory.AppendSegment(current_coordinate, path_data); + ++added_element_count; + } + ++added_element_count; + BOOST_ASSERT((route_leg.size() + 1) == added_element_count); + return added_element_count; + } + + void Run(const RawRouteData &raw_route, + const PhantomNodes &phantom_nodes, + http::Reply &reply) + { + JSON::Object json_result; + + if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) + { + // We do not need to do much, if there is no route ;-) + json_result.values["status"] = 207; + json_result.values["status_message"] = "Cannot find route between points"; + JSON::render(reply.content, json_result); + return; + } + + // check if first segment is non-zero + std::string road_name = + facade->GetEscapedNameForNameID(phantom_nodes.source_phantom.name_id); + + BOOST_ASSERT(raw_route.unpacked_path_segments.size() == + raw_route.segment_end_coordinates.size()); + + description_factory.SetStartSegment(phantom_nodes.source_phantom); + json_result.values["status"] = 0; + json_result.values["status_message"] = "Found route between points"; + + // for each unpacked segment add the leg to the description + for (unsigned i = 0; i < raw_route.unpacked_path_segments.size(); ++i) + { + const int added_segments = DescribeLeg(raw_route.unpacked_path_segments[i], + raw_route.segment_end_coordinates[i]); + BOOST_ASSERT(0 < added_segments); + shortest_leg_end_indices.emplace_back(added_segments + shortest_leg_end_indices.back()); + } + description_factory.SetEndSegment(phantom_nodes.target_phantom); + description_factory.Run(facade, config.zoom_level); + + if (config.geometry) + { + JSON::Value route_geometry = description_factory.AppendEncodedPolylineString(config.encode_geometry); + json_result.values["route_geometry"] = route_geometry; + } + if (config.instructions) + { + JSON::Array json_route_instructions; + BuildTextualDescription(description_factory, + json_route_instructions, + raw_route.shortest_path_length, + shortest_path_segments); + json_result.values["route_instructions"] = json_route_instructions; + } + description_factory.BuildRouteSummary(description_factory.entireLength, + raw_route.shortest_path_length); + JSON::Object json_route_summary; + json_route_summary.values["total_distance"] = description_factory.summary.distance; + json_route_summary.values["total_time"] = description_factory.summary.duration; + json_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(description_factory.summary.source_name_id); + json_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(description_factory.summary.target_name_id); + json_result.values["route_summary"] = json_route_summary; + + BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); + + JSON::Array json_via_points_array; + JSON::Array json_first_coordinate; + json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lat/COORDINATE_PRECISION); + json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lon/COORDINATE_PRECISION); + json_via_points_array.values.push_back(json_first_coordinate); + for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) + { + std::string tmp; + JSON::Array json_coordinate; + json_coordinate.values.push_back(nodes.target_phantom.location.lat/COORDINATE_PRECISION); + json_coordinate.values.push_back(nodes.target_phantom.location.lon/COORDINATE_PRECISION); + json_via_points_array.values.push_back(json_coordinate); + } + json_result.values["via_points"] = json_via_points_array; + + JSON::Array json_via_indices_array; + json_via_indices_array.values.insert(json_via_indices_array.values.end(), shortest_leg_end_indices.begin(), shortest_leg_end_indices.end()); + json_result.values["via_indices"] = json_via_indices_array; + + // only one alternative route is computed at this time, so this is hardcoded + if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) + { + json_result.values["found_alternative"] = JSON::True(); + alternate_description_factory.SetStartSegment(phantom_nodes.source_phantom); + // Get all the coordinates for the computed route + for (const PathData &path_data : raw_route.unpacked_alternative) + { + current = facade->GetCoordinateOfNode(path_data.node); + alternate_description_factory.AppendSegment(current, path_data); + } + alternate_description_factory.Run(facade, config.zoom_level); + + if (config.geometry) + { + JSON::Value alternate_geometry_string = alternate_description_factory.AppendEncodedPolylineString(config.encode_geometry); + JSON::Array json_alternate_geometries_array; + json_alternate_geometries_array.values.push_back(alternate_geometry_string); + json_result.values["alternative_geometries"] = json_alternate_geometries_array; + } + // Generate instructions for each alternative (simulated here) + JSON::Array json_alt_instructions; + JSON::Array json_current_alt_instructions; + if (config.instructions) + { + BuildTextualDescription(alternate_description_factory, + json_current_alt_instructions, + raw_route.alternative_path_length, + alternative_path_segments); + json_alt_instructions.values.push_back(json_current_alt_instructions); + json_result.values["alternative_instructions"] = json_alt_instructions; + } + alternate_description_factory.BuildRouteSummary( + alternate_description_factory.entireLength, raw_route.alternative_path_length); + + JSON::Object json_alternate_route_summary; + JSON::Array json_alternate_route_summary_array; + json_alternate_route_summary.values["total_distance"] = alternate_description_factory.summary.distance; + json_alternate_route_summary.values["total_time"] = alternate_description_factory.summary.duration; + json_alternate_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.source_name_id); + json_alternate_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.target_name_id); + json_alternate_route_summary_array.values.push_back(json_alternate_route_summary); + json_result.values["alternative_summaries"] = json_alternate_route_summary_array; + + JSON::Array json_altenative_indices_array; + json_altenative_indices_array.values.push_back(0); + json_altenative_indices_array.values.push_back(alternate_description_factory.path_description.size()); + json_result.values["alternative_indices"] = json_altenative_indices_array; + } else { + json_result.values["found_alternative"] = JSON::False(); + } + + // Get Names for both routes + RouteNames route_names = GenerateRouteNames(shortest_path_segments, alternative_path_segments, facade); + JSON::Array json_route_names; + json_route_names.values.push_back(route_names.shortest_path_name_1); + json_route_names.values.push_back(route_names.shortest_path_name_2); + json_result.values["route_name"] = json_route_names; + + if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) + { + JSON::Array json_alternate_names_array; + JSON::Array json_alternate_names; + json_alternate_names.values.push_back(route_names.alternative_path_name_1); + json_alternate_names.values.push_back(route_names.alternative_path_name_2); + json_alternate_names_array.values.push_back(json_alternate_names); + json_result.values["alternative_names"] = json_alternate_names_array; + } + + JSON::Object json_hint_object; + json_hint_object.values["checksum"] = raw_route.check_sum; + JSON::Array json_location_hint_array; + std::string hint; + for (unsigned i = 0; i < raw_route.segment_end_coordinates.size(); ++i) + { + EncodeObjectToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint); + json_location_hint_array.values.push_back(hint); + } + EncodeObjectToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint); + json_location_hint_array.values.push_back(hint); + json_hint_object.values["locations"] = json_location_hint_array; + json_result.values["hint_data"] = json_hint_object; + + // render the content to the output array + TIMER_START(route_render); + JSON::render(reply.content, json_result); + TIMER_STOP(route_render); + SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render); + } + + // TODO: reorder parameters + inline void BuildTextualDescription(DescriptionFactory &description_factory, + JSON::Array & json_instruction_array, + const int route_length, + std::vector &route_segments_list) + { + // Segment information has following format: + //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] + unsigned necessary_segments_running_index = 0; + round_about.leave_at_exit = 0; + round_about.name_id = 0; + std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; + + // Fetch data from Factory and generate a string from it. + for (const SegmentInformation &segment : description_factory.path_description) + { + JSON::Array json_instruction_row; + TurnInstruction current_instruction = segment.turn_instruction; + entered_restricted_area_count += (current_instruction != segment.turn_instruction); + if (TurnInstructionsClass::TurnIsNecessary(current_instruction)) + { + if (TurnInstruction::EnterRoundAbout == current_instruction) + { + round_about.name_id = segment.name_id; + round_about.start_index = necessary_segments_running_index; + } + else + { + std::string current_turn_instruction; + if (TurnInstruction::LeaveRoundAbout == current_instruction) + { + temp_instruction = IntToString(as_integer(TurnInstruction::EnterRoundAbout)); + current_turn_instruction += temp_instruction; + current_turn_instruction += "-"; + temp_instruction = IntToString(round_about.leave_at_exit + 1); + current_turn_instruction += temp_instruction; + round_about.leave_at_exit = 0; + } + else + { + temp_instruction = IntToString(as_integer(current_instruction)); + current_turn_instruction += temp_instruction; + } + json_instruction_row.values.push_back(current_turn_instruction); + + json_instruction_row.values.push_back(facade->GetEscapedNameForNameID(segment.name_id)); + json_instruction_row.values.push_back(std::round(segment.length)); + json_instruction_row.values.push_back(necessary_segments_running_index); + json_instruction_row.values.push_back(round(segment.duration / 10)); + json_instruction_row.values.push_back(IntToString(segment.length)+"m"); + int bearing_value = round(segment.bearing / 10.); + json_instruction_row.values.push_back(Azimuth::Get(bearing_value)); + json_instruction_row.values.push_back(bearing_value); + + route_segments_list.emplace_back( + segment.name_id, segment.length, route_segments_list.size()); + json_instruction_array.values.push_back(json_instruction_row); + } + } + else if (TurnInstruction::StayOnRoundAbout == current_instruction) + { + ++round_about.leave_at_exit; + } + if (segment.necessary) + { + ++necessary_segments_running_index; + } + } + + //TODO: check if this in an invariant + if (INVALID_EDGE_WEIGHT != route_length) + { + JSON::Array json_last_instruction_row; + temp_instruction = IntToString(as_integer(TurnInstruction::ReachedYourDestination)); + json_last_instruction_row.values.push_back(temp_instruction); + json_last_instruction_row.values.push_back(""); + json_last_instruction_row.values.push_back(0); + json_last_instruction_row.values.push_back(necessary_segments_running_index - 1); + json_last_instruction_row.values.push_back(0); + json_last_instruction_row.values.push_back("0m"); + json_last_instruction_row.values.push_back(Azimuth::Get(0.0)); + json_last_instruction_row.values.push_back(0.); + json_instruction_array.values.push_back(json_last_instruction_row); + } + } +}; + +#endif /* JSON_DESCRIPTOR_H_ */ diff --git a/Include/osrm/Coordinate.h b/Include/osrm/Coordinate.h index 18d37366b..d36cd05bc 100644 --- a/Include/osrm/Coordinate.h +++ b/Include/osrm/Coordinate.h @@ -74,7 +74,14 @@ struct FixedPointCoordinate FixedPointCoordinate &nearest_location, double &r); + static double GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B); + + double GetBearing(const FixedPointCoordinate &other) const; + void Output(std::ostream &out) const; + + static double DegreeToRadian(const double degree); + static double RadianToDegree(const double radian); }; inline std::ostream &operator<<(std::ostream &o, FixedPointCoordinate const &c)