osrm-backend/Descriptors/JSONDescriptor.h

458 lines
20 KiB
C
Raw Normal View History

/*
open source routing machine
Copyright (C) Dennis Luxen, others 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU AFFERO General Public License as published by
the Free Software Foundation; either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
or see http://www.gnu.org/licenses/agpl.txt.
*/
#ifndef JSON_DESCRIPTOR_H_
#define JSON_DESCRIPTOR_H_
#include "BaseDescriptor.h"
#include "DescriptionFactory.h"
#include "../Algorithms/ObjectToBase64.h"
#include "../DataStructures/SegmentInformation.h"
#include "../DataStructures/TurnInstructions.h"
#include "../Util/Azimuth.h"
#include "../Util/StringUtil.h"
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <algorithm>
2013-09-19 12:54:30 -04:00
template<class DataFacadeT>
class JSONDescriptor : public BaseDescriptor<DataFacadeT> {
private:
2013-09-19 16:10:49 -04:00
DescriptorConfig config;
2013-09-19 13:07:18 -04:00
DescriptionFactory description_factory;
DescriptionFactory alternateDescriptionFactory;
2013-08-14 07:12:28 -04:00
FixedPointCoordinate current;
2013-09-19 13:07:18 -04:00
unsigned entered_restricted_area_count;
2013-06-27 11:44:32 -04:00
struct RoundAbout{
RoundAbout() :
2013-09-19 13:07:18 -04:00
start_index(INT_MAX),
name_id(INT_MAX),
leave_at_exit(INT_MAX)
2013-06-27 11:44:32 -04:00
{}
2013-09-19 13:07:18 -04:00
int start_index;
int name_id;
int leave_at_exit;
2012-03-23 12:44:56 -04:00
} roundAbout;
struct Segment {
2013-09-19 13:07:18 -04:00
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;
};
2013-09-19 13:07:18 -04:00
std::vector<Segment> shortest_path_segments, alternative_path_segments;
struct RouteNames {
std::string shortestPathName1;
std::string shortestPathName2;
std::string alternativePathName1;
std::string alternativePathName2;
};
public:
2013-09-19 13:07:18 -04:00
JSONDescriptor() : entered_restricted_area_count(0) {}
2013-09-19 16:10:49 -04:00
void SetConfig(const DescriptorConfig & c) { config = c; }
2012-03-23 12:44:56 -04:00
//TODO: reorder parameters
2013-09-19 12:54:30 -04:00
void Run(
http::Reply & reply,
2013-09-19 13:07:18 -04:00
const RawRouteData & raw_route_information,
PhantomNodes & phantom_nodes,
2013-09-19 12:54:30 -04:00
const DataFacadeT * facade
) {
2012-03-23 12:44:56 -04:00
WriteHeaderToOutput(reply.content);
2013-09-19 13:07:18 -04:00
if(raw_route_information.lengthOfShortestPath != INT_MAX) {
description_factory.SetStartSegment(phantom_nodes.startPhantom);
2012-03-23 12:44:56 -04:00
reply.content += "0,"
"\"status_message\": \"Found route between points\",";
//Get all the coordinates for the computed route
2013-09-19 13:07:18 -04:00
BOOST_FOREACH(const _PathData & path_data, raw_route_information.computedShortestPath) {
current = facade->GetCoordinateOfNode(path_data.node);
description_factory.AppendSegment(current, path_data );
2012-03-23 12:44:56 -04:00
}
2013-09-19 13:07:18 -04:00
description_factory.SetEndSegment(phantom_nodes.targetPhantom);
2012-03-23 12:44:56 -04:00
} else {
//We do not need to do much, if there is no route ;-)
reply.content += "207,"
"\"status_message\": \"Cannot find route between points\",";
}
2013-09-19 16:28:02 -04:00
description_factory.Run(facade, config.zoom_level);
2012-03-23 12:44:56 -04:00
reply.content += "\"route_geometry\": ";
if(config.geometry) {
2013-09-19 16:28:02 -04:00
description_factory.AppendEncodedPolylineString(
reply.content,
config.encode_geometry
);
2012-03-23 12:44:56 -04:00
} else {
reply.content += "[]";
}
reply.content += ","
"\"route_instructions\": [";
2013-09-19 13:07:18 -04:00
entered_restricted_area_count = 0;
2012-03-23 12:44:56 -04:00
if(config.instructions) {
2013-09-19 13:07:18 -04:00
BuildTextualDescription(
description_factory,
reply,
raw_route_information.lengthOfShortestPath,
facade,
shortest_path_segments
);
2012-03-23 12:44:56 -04:00
} else {
2013-09-19 13:07:18 -04:00
BOOST_FOREACH(
const SegmentInformation & segment,
description_factory.pathDescription
) {
TurnInstruction current_instruction = segment.turnInstruction & TurnInstructions.InverseAccessRestrictionFlag;
entered_restricted_area_count += (current_instruction != segment.turnInstruction);
2012-03-23 12:44:56 -04:00
}
}
reply.content += "],";
2013-09-19 13:07:18 -04:00
description_factory.BuildRouteSummary(
description_factory.entireLength,
raw_route_information.lengthOfShortestPath - ( entered_restricted_area_count*TurnInstructions.AccessRestrictionPenalty)
);
2012-03-23 12:44:56 -04:00
reply.content += "\"route_summary\":";
reply.content += "{";
reply.content += "\"total_distance\":";
2013-09-19 13:07:18 -04:00
reply.content += description_factory.summary.lengthString;
2012-03-23 12:44:56 -04:00
reply.content += ","
"\"total_time\":";
2013-09-19 13:07:18 -04:00
reply.content += description_factory.summary.durationString;
2012-03-23 12:44:56 -04:00
reply.content += ","
"\"start_point\":\"";
2013-09-19 13:07:18 -04:00
reply.content += facade->GetEscapedNameForNameID(
description_factory.summary.startName
);
2012-03-23 12:44:56 -04:00
reply.content += "\","
"\"end_point\":\"";
2013-09-19 13:07:18 -04:00
reply.content += facade->GetEscapedNameForNameID(
description_factory.summary.destName
);
2012-03-23 12:44:56 -04:00
reply.content += "\"";
reply.content += "}";
reply.content +=",";
//only one alternative route is computed at this time, so this is hardcoded
2013-09-19 13:07:18 -04:00
if(raw_route_information.lengthOfAlternativePath != INT_MAX) {
alternateDescriptionFactory.SetStartSegment(phantom_nodes.startPhantom);
//Get all the coordinates for the computed route
2013-09-19 13:07:18 -04:00
BOOST_FOREACH(const _PathData & path_data, raw_route_information.computedAlternativePath) {
current = facade->GetCoordinateOfNode(path_data.node);
alternateDescriptionFactory.AppendSegment(current, path_data );
}
2013-09-19 13:07:18 -04:00
alternateDescriptionFactory.SetEndSegment(phantom_nodes.targetPhantom);
}
2013-09-19 16:28:02 -04:00
alternateDescriptionFactory.Run(facade, config.zoom_level);
//give an array of alternative routes
reply.content += "\"alternative_geometries\": [";
2013-09-19 13:07:18 -04:00
if(config.geometry && INT_MAX != raw_route_information.lengthOfAlternativePath) {
//Generate the linestrings for each alternative
2013-09-19 16:28:02 -04:00
alternateDescriptionFactory.AppendEncodedPolylineString(
reply.content,
config.encode_geometry
);
}
reply.content += "],";
reply.content += "\"alternative_instructions\":[";
2013-09-19 13:07:18 -04:00
entered_restricted_area_count = 0;
if(INT_MAX != raw_route_information.lengthOfAlternativePath) {
reply.content += "[";
//Generate instructions for each alternative
2012-07-11 04:44:11 -04:00
if(config.instructions) {
2013-09-19 12:54:30 -04:00
BuildTextualDescription(
alternateDescriptionFactory,
reply,
2013-09-19 13:07:18 -04:00
raw_route_information.lengthOfAlternativePath,
2013-09-19 12:54:30 -04:00
facade,
2013-09-19 13:07:18 -04:00
alternative_path_segments
2013-09-19 12:54:30 -04:00
);
2012-07-11 04:44:11 -04:00
} else {
BOOST_FOREACH(const SegmentInformation & segment, alternateDescriptionFactory.pathDescription) {
2013-09-19 13:07:18 -04:00
TurnInstruction current_instruction = segment.turnInstruction & TurnInstructions.InverseAccessRestrictionFlag;
entered_restricted_area_count += (current_instruction != segment.turnInstruction);
2012-07-11 04:44:11 -04:00
}
}
reply.content += "]";
}
reply.content += "],";
reply.content += "\"alternative_summaries\":[";
2013-09-19 13:07:18 -04:00
if(INT_MAX != raw_route_information.lengthOfAlternativePath) {
//Generate route summary (length, duration) for each alternative
2013-09-19 13:07:18 -04:00
alternateDescriptionFactory.BuildRouteSummary(alternateDescriptionFactory.entireLength, raw_route_information.lengthOfAlternativePath - ( entered_restricted_area_count*TurnInstructions.AccessRestrictionPenalty));
reply.content += "{";
reply.content += "\"total_distance\":";
reply.content += alternateDescriptionFactory.summary.lengthString;
reply.content += ","
"\"total_time\":";
reply.content += alternateDescriptionFactory.summary.durationString;
reply.content += ","
"\"start_point\":\"";
2013-09-19 13:07:18 -04:00
reply.content += facade->GetEscapedNameForNameID(description_factory.summary.startName);
reply.content += "\","
"\"end_point\":\"";
2013-09-19 13:07:18 -04:00
reply.content += facade->GetEscapedNameForNameID(description_factory.summary.destName);
reply.content += "\"";
reply.content += "}";
}
reply.content += "],";
2012-03-23 12:44:56 -04:00
//Get Names for both routes
RouteNames routeNames;
2013-09-19 13:07:18 -04:00
GetRouteNames(shortest_path_segments, alternative_path_segments, facade, routeNames);
reply.content += "\"route_name\":[\"";
reply.content += routeNames.shortestPathName1;
reply.content += "\",\"";
reply.content += routeNames.shortestPathName2;
reply.content += "\"],"
"\"alternative_names\":[";
reply.content += "[\"";
reply.content += routeNames.alternativePathName1;
reply.content += "\",\"";
reply.content += routeNames.alternativePathName2;
reply.content += "\"]";
reply.content += "],";
2012-03-23 12:44:56 -04:00
//list all viapoints so that the client may display it
reply.content += "\"via_points\":[";
2012-02-17 02:34:52 -05:00
std::string tmp;
2013-09-19 13:07:18 -04:00
if(config.geometry && INT_MAX != raw_route_information.lengthOfShortestPath) {
for(unsigned i = 0; i < raw_route_information.segmentEndCoordinates.size(); ++i) {
2012-03-23 12:44:56 -04:00
reply.content += "[";
2013-09-19 13:07:18 -04:00
if(raw_route_information.segmentEndCoordinates[i].startPhantom.location.isSet())
convertInternalReversedCoordinateToString(raw_route_information.segmentEndCoordinates[i].startPhantom.location, tmp);
2012-03-23 12:44:56 -04:00
else
2013-09-19 13:07:18 -04:00
convertInternalReversedCoordinateToString(raw_route_information.rawViaNodeCoordinates[i], tmp);
2012-03-23 12:44:56 -04:00
reply.content += tmp;
2012-05-15 10:42:31 -04:00
reply.content += "],";
2012-03-23 12:44:56 -04:00
}
2012-05-15 10:42:31 -04:00
reply.content += "[";
2013-09-19 13:07:18 -04:00
if(raw_route_information.segmentEndCoordinates.back().startPhantom.location.isSet())
convertInternalReversedCoordinateToString(raw_route_information.segmentEndCoordinates.back().targetPhantom.location, tmp);
2012-05-15 10:42:31 -04:00
else
2013-09-19 13:07:18 -04:00
convertInternalReversedCoordinateToString(raw_route_information.rawViaNodeCoordinates.back(), tmp);
2012-05-15 10:42:31 -04:00
reply.content += tmp;
reply.content += "]";
2012-03-23 12:44:56 -04:00
}
reply.content += "],";
reply.content += "\"hint_data\": {";
2012-02-17 02:34:52 -05:00
reply.content += "\"checksum\":";
2013-09-19 13:07:18 -04:00
intToString(raw_route_information.checkSum, tmp);
2012-02-17 02:34:52 -05:00
reply.content += tmp;
reply.content += ", \"locations\": [";
2012-05-15 10:42:31 -04:00
std::string hint;
2013-09-19 13:07:18 -04:00
for(unsigned i = 0; i < raw_route_information.segmentEndCoordinates.size(); ++i) {
reply.content += "\"";
2013-09-19 13:07:18 -04:00
EncodeObjectToBase64(raw_route_information.segmentEndCoordinates[i].startPhantom, hint);
reply.content += hint;
reply.content += "\", ";
}
2013-09-19 13:07:18 -04:00
EncodeObjectToBase64(raw_route_information.segmentEndCoordinates.back().targetPhantom, hint);
reply.content += "\"";
reply.content += hint;
reply.content += "\"]";
2012-02-17 02:34:52 -05:00
reply.content += "},";
2012-03-23 12:44:56 -04:00
reply.content += "\"transactionId\": \"OSRM Routing Engine JSON Descriptor (v0.3)\"";
reply.content += "}";
}
2013-09-19 12:54:30 -04:00
// construct routes names
void GetRouteNames(
2013-09-19 13:07:18 -04:00
std::vector<Segment> & shortest_path_segments,
std::vector<Segment> & alternative_path_segments,
2013-09-19 12:54:30 -04:00
const DataFacadeT * facade,
RouteNames & routeNames
) {
Segment shortestSegment1, shortestSegment2;
Segment alternativeSegment1, alternativeSegment2;
2013-09-19 13:07:18 -04:00
if(0 < shortest_path_segments.size()) {
sort(shortest_path_segments.begin(), shortest_path_segments.end(), boost::bind(&Segment::length, _1) > boost::bind(&Segment::length, _2) );
shortestSegment1 = shortest_path_segments[0];
if(0 < alternative_path_segments.size()) {
sort(alternative_path_segments.begin(), alternative_path_segments.end(), boost::bind(&Segment::length, _1) > boost::bind(&Segment::length, _2) );
alternativeSegment1 = alternative_path_segments[0];
}
2013-09-19 13:07:18 -04:00
std::vector<Segment> shortestDifference(shortest_path_segments.size());
std::vector<Segment> alternativeDifference(alternative_path_segments.size());
std::set_difference(shortest_path_segments.begin(), shortest_path_segments.end(), alternative_path_segments.begin(), alternative_path_segments.end(), shortestDifference.begin(), boost::bind(&Segment::name_id, _1) < boost::bind(&Segment::name_id, _2) );
2013-01-06 06:39:12 -05:00
int size_of_difference = shortestDifference.size();
if(0 < size_of_difference ) {
int i = 0;
2013-09-19 13:07:18 -04:00
while( i < size_of_difference && shortestDifference[i].name_id == shortest_path_segments[0].name_id) {
++i;
}
2013-01-06 06:39:12 -05:00
if(i < size_of_difference ) {
shortestSegment2 = shortestDifference[i];
}
}
2013-09-19 13:07:18 -04:00
std::set_difference(alternative_path_segments.begin(), alternative_path_segments.end(), shortest_path_segments.begin(), shortest_path_segments.end(), alternativeDifference.begin(), boost::bind(&Segment::name_id, _1) < boost::bind(&Segment::name_id, _2) );
2013-01-06 06:39:12 -05:00
size_of_difference = alternativeDifference.size();
if(0 < size_of_difference ) {
int i = 0;
2013-09-19 13:07:18 -04:00
while( i < size_of_difference && alternativeDifference[i].name_id == alternative_path_segments[0].name_id) {
++i;
}
2013-01-06 06:39:12 -05:00
if(i < size_of_difference ) {
alternativeSegment2 = alternativeDifference[i];
}
}
if(shortestSegment1.position > shortestSegment2.position)
std::swap(shortestSegment1, shortestSegment2);
if(alternativeSegment1.position > alternativeSegment2.position)
std::swap(alternativeSegment1, alternativeSegment2);
2013-09-19 12:54:30 -04:00
routeNames.shortestPathName1 = facade->GetEscapedNameForNameID(
2013-09-19 13:07:18 -04:00
shortestSegment1.name_id
2013-09-19 12:54:30 -04:00
);
routeNames.shortestPathName2 = facade->GetEscapedNameForNameID(
2013-09-19 13:07:18 -04:00
shortestSegment2.name_id
2013-09-19 12:54:30 -04:00
);
routeNames.alternativePathName1 = facade->GetEscapedNameForNameID(
2013-09-19 13:07:18 -04:00
alternativeSegment1.name_id
2013-09-19 12:54:30 -04:00
);
routeNames.alternativePathName2 = facade->GetEscapedNameForNameID(
2013-09-19 13:07:18 -04:00
alternativeSegment2.name_id
2013-09-19 12:54:30 -04:00
);
}
2012-03-23 12:44:56 -04:00
}
inline void WriteHeaderToOutput(std::string & output) {
output += "{"
"\"version\": 0.3,"
"\"status\":";
}
2013-09-19 12:54:30 -04:00
//TODO: reorder parameters
inline void BuildTextualDescription(
2013-09-19 13:07:18 -04:00
DescriptionFactory & description_factory,
2013-09-19 12:54:30 -04:00
http::Reply & reply,
2013-09-19 13:07:18 -04:00
const int route_length,
2013-09-19 12:54:30 -04:00
const DataFacadeT * facade,
2013-09-19 13:07:18 -04:00
std::vector<Segment> & route_segments_list
2013-09-19 12:54:30 -04:00
) {
//Segment information has following format:
//["instruction","streetname",length,position,time,"length","earth_direction",azimuth]
//Example: ["Turn left","High Street",200,4,10,"200m","NE",22.5]
//See also: http://developers.cloudmade.com/wiki/navengine/JSON_format
unsigned prefixSumOfNecessarySegments = 0;
2013-09-19 13:07:18 -04:00
roundAbout.leave_at_exit = 0;
roundAbout.name_id = 0;
std::string tmpDist, tmpLength, tmpDuration, tmpBearing, tmpInstruction;
//Fetch data from Factory and generate a string from it.
2013-09-19 13:07:18 -04:00
BOOST_FOREACH(const SegmentInformation & segment, description_factory.pathDescription) {
TurnInstruction current_instruction = segment.turnInstruction & TurnInstructions.InverseAccessRestrictionFlag;
entered_restricted_area_count += (current_instruction != segment.turnInstruction);
if(TurnInstructions.TurnIsNecessary( current_instruction) ) {
if(TurnInstructions.EnterRoundAbout == current_instruction) {
roundAbout.name_id = segment.nameID;
roundAbout.start_index = prefixSumOfNecessarySegments;
} else {
if(0 != prefixSumOfNecessarySegments){
reply.content += ",";
}
reply.content += "[\"";
2013-09-19 13:07:18 -04:00
if(TurnInstructions.LeaveRoundAbout == current_instruction) {
intToString(TurnInstructions.EnterRoundAbout, tmpInstruction);
reply.content += tmpInstruction;
reply.content += "-";
2013-09-19 13:07:18 -04:00
intToString(roundAbout.leave_at_exit+1, tmpInstruction);
reply.content += tmpInstruction;
2013-09-19 13:07:18 -04:00
roundAbout.leave_at_exit = 0;
} else {
2013-09-19 13:07:18 -04:00
intToString(current_instruction, tmpInstruction);
reply.content += tmpInstruction;
}
reply.content += "\",\"";
2013-09-19 12:54:30 -04:00
reply.content += facade->GetEscapedNameForNameID(segment.nameID);
reply.content += "\",";
intToString(segment.length, tmpDist);
reply.content += tmpDist;
reply.content += ",";
intToString(prefixSumOfNecessarySegments, tmpLength);
reply.content += tmpLength;
reply.content += ",";
intToString(segment.duration/10, tmpDuration);
reply.content += tmpDuration;
reply.content += ",\"";
intToString(segment.length, tmpLength);
reply.content += tmpLength;
reply.content += "m\",\"";
reply.content += Azimuth::Get(segment.bearing);
reply.content += "\",";
intToString(round(segment.bearing), tmpBearing);
reply.content += tmpBearing;
reply.content += "]";
2013-09-19 13:07:18 -04:00
route_segments_list.push_back(
Segment(
segment.nameID,
segment.length,
route_segments_list.size()
)
);
}
2013-09-19 13:07:18 -04:00
} else if(TurnInstructions.StayOnRoundAbout == current_instruction) {
++roundAbout.leave_at_exit;
}
if(segment.necessary)
++prefixSumOfNecessarySegments;
}
2013-09-19 13:07:18 -04:00
if(INT_MAX != route_length) {
reply.content += ",[\"";
intToString(TurnInstructions.ReachedYourDestination, tmpInstruction);
reply.content += tmpInstruction;
reply.content += "\",\"";
reply.content += "\",";
reply.content += "0";
reply.content += ",";
intToString(prefixSumOfNecessarySegments-1, tmpLength);
reply.content += tmpLength;
reply.content += ",";
reply.content += "0";
reply.content += ",\"";
reply.content += "\",\"";
reply.content += Azimuth::Get(0.0);
reply.content += "\",";
reply.content += "0.0";
reply.content += "]";
}
}
};
#endif /* JSON_DESCRIPTOR_H_ */