/* 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 #include #include template class JSONDescriptor : public BaseDescriptor { private: DescriptorConfig config; DescriptionFactory description_factory; DescriptionFactory alternateDescriptionFactory; 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; } roundAbout; 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; struct RouteNames { std::string shortestPathName1; std::string shortestPathName2; std::string alternativePathName1; std::string alternativePathName2; }; public: JSONDescriptor() : entered_restricted_area_count(0) {} void SetConfig(const DescriptorConfig & c) { config = c; } //TODO: reorder parameters void Run( http::Reply & reply, const RawRouteData & raw_route_information, PhantomNodes & phantom_nodes, const DataFacadeT * facade ) { WriteHeaderToOutput(reply.content); if(raw_route_information.lengthOfShortestPath != INT_MAX) { description_factory.SetStartSegment(phantom_nodes.startPhantom); reply.content += "0," "\"status_message\": \"Found route between points\","; //Get all the coordinates for the computed route BOOST_FOREACH(const _PathData & path_data, raw_route_information.computedShortestPath) { current = facade->GetCoordinateOfNode(path_data.node); description_factory.AppendSegment(current, path_data ); } description_factory.SetEndSegment(phantom_nodes.targetPhantom); } else { //We do not need to do much, if there is no route ;-) reply.content += "207," "\"status_message\": \"Cannot find route between points\","; } description_factory.Run(facade, config.zoom_level); reply.content += "\"route_geometry\": "; if(config.geometry) { description_factory.AppendEncodedPolylineString( reply.content, config.encode_geometry ); } else { reply.content += "[]"; } reply.content += "," "\"route_instructions\": ["; entered_restricted_area_count = 0; if(config.instructions) { BuildTextualDescription( description_factory, reply, raw_route_information.lengthOfShortestPath, facade, shortest_path_segments ); } else { BOOST_FOREACH( const SegmentInformation & segment, description_factory.pathDescription ) { TurnInstruction current_instruction = segment.turnInstruction & TurnInstructions.InverseAccessRestrictionFlag; entered_restricted_area_count += (current_instruction != segment.turnInstruction); } } reply.content += "],"; description_factory.BuildRouteSummary( description_factory.entireLength, raw_route_information.lengthOfShortestPath - ( entered_restricted_area_count*TurnInstructions.AccessRestrictionPenalty) ); reply.content += "\"route_summary\":"; reply.content += "{"; reply.content += "\"total_distance\":"; reply.content += description_factory.summary.lengthString; reply.content += "," "\"total_time\":"; reply.content += description_factory.summary.durationString; reply.content += "," "\"start_point\":\""; reply.content += facade->GetEscapedNameForNameID( description_factory.summary.startName ); reply.content += "\"," "\"end_point\":\""; reply.content += facade->GetEscapedNameForNameID( description_factory.summary.destName ); reply.content += "\""; reply.content += "}"; reply.content +=","; //only one alternative route is computed at this time, so this is hardcoded if(raw_route_information.lengthOfAlternativePath != INT_MAX) { alternateDescriptionFactory.SetStartSegment(phantom_nodes.startPhantom); //Get all the coordinates for the computed route BOOST_FOREACH(const _PathData & path_data, raw_route_information.computedAlternativePath) { current = facade->GetCoordinateOfNode(path_data.node); alternateDescriptionFactory.AppendSegment(current, path_data ); } alternateDescriptionFactory.SetEndSegment(phantom_nodes.targetPhantom); } alternateDescriptionFactory.Run(facade, config.zoom_level); //give an array of alternative routes reply.content += "\"alternative_geometries\": ["; if(config.geometry && INT_MAX != raw_route_information.lengthOfAlternativePath) { //Generate the linestrings for each alternative alternateDescriptionFactory.AppendEncodedPolylineString( reply.content, config.encode_geometry ); } reply.content += "],"; reply.content += "\"alternative_instructions\":["; entered_restricted_area_count = 0; if(INT_MAX != raw_route_information.lengthOfAlternativePath) { reply.content += "["; //Generate instructions for each alternative if(config.instructions) { BuildTextualDescription( alternateDescriptionFactory, reply, raw_route_information.lengthOfAlternativePath, facade, alternative_path_segments ); } else { BOOST_FOREACH(const SegmentInformation & segment, alternateDescriptionFactory.pathDescription) { TurnInstruction current_instruction = segment.turnInstruction & TurnInstructions.InverseAccessRestrictionFlag; entered_restricted_area_count += (current_instruction != segment.turnInstruction); } } reply.content += "]"; } reply.content += "],"; reply.content += "\"alternative_summaries\":["; if(INT_MAX != raw_route_information.lengthOfAlternativePath) { //Generate route summary (length, duration) for each alternative 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\":\""; reply.content += facade->GetEscapedNameForNameID(description_factory.summary.startName); reply.content += "\"," "\"end_point\":\""; reply.content += facade->GetEscapedNameForNameID(description_factory.summary.destName); reply.content += "\""; reply.content += "}"; } reply.content += "],"; //Get Names for both routes RouteNames routeNames; 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 += "],"; //list all viapoints so that the client may display it reply.content += "\"via_points\":["; std::string tmp; if(config.geometry && INT_MAX != raw_route_information.lengthOfShortestPath) { for(unsigned i = 0; i < raw_route_information.segmentEndCoordinates.size(); ++i) { reply.content += "["; if(raw_route_information.segmentEndCoordinates[i].startPhantom.location.isSet()) convertInternalReversedCoordinateToString(raw_route_information.segmentEndCoordinates[i].startPhantom.location, tmp); else convertInternalReversedCoordinateToString(raw_route_information.rawViaNodeCoordinates[i], tmp); reply.content += tmp; reply.content += "],"; } reply.content += "["; if(raw_route_information.segmentEndCoordinates.back().startPhantom.location.isSet()) convertInternalReversedCoordinateToString(raw_route_information.segmentEndCoordinates.back().targetPhantom.location, tmp); else convertInternalReversedCoordinateToString(raw_route_information.rawViaNodeCoordinates.back(), tmp); reply.content += tmp; reply.content += "]"; } reply.content += "],"; reply.content += "\"hint_data\": {"; reply.content += "\"checksum\":"; intToString(raw_route_information.checkSum, tmp); reply.content += tmp; reply.content += ", \"locations\": ["; std::string hint; for(unsigned i = 0; i < raw_route_information.segmentEndCoordinates.size(); ++i) { reply.content += "\""; EncodeObjectToBase64(raw_route_information.segmentEndCoordinates[i].startPhantom, hint); reply.content += hint; reply.content += "\", "; } EncodeObjectToBase64(raw_route_information.segmentEndCoordinates.back().targetPhantom, hint); reply.content += "\""; reply.content += hint; reply.content += "\"]"; reply.content += "},"; reply.content += "\"transactionId\": \"OSRM Routing Engine JSON Descriptor (v0.3)\""; reply.content += "}"; } // construct routes names void GetRouteNames( std::vector & shortest_path_segments, std::vector & alternative_path_segments, const DataFacadeT * facade, RouteNames & routeNames ) { Segment shortestSegment1, shortestSegment2; Segment alternativeSegment1, alternativeSegment2; 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]; } std::vector shortestDifference(shortest_path_segments.size()); std::vector 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) ); int size_of_difference = shortestDifference.size(); if(0 < size_of_difference ) { int i = 0; while( i < size_of_difference && shortestDifference[i].name_id == shortest_path_segments[0].name_id) { ++i; } if(i < size_of_difference ) { shortestSegment2 = shortestDifference[i]; } } 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) ); size_of_difference = alternativeDifference.size(); if(0 < size_of_difference ) { int i = 0; while( i < size_of_difference && alternativeDifference[i].name_id == alternative_path_segments[0].name_id) { ++i; } 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); routeNames.shortestPathName1 = facade->GetEscapedNameForNameID( shortestSegment1.name_id ); routeNames.shortestPathName2 = facade->GetEscapedNameForNameID( shortestSegment2.name_id ); routeNames.alternativePathName1 = facade->GetEscapedNameForNameID( alternativeSegment1.name_id ); routeNames.alternativePathName2 = facade->GetEscapedNameForNameID( alternativeSegment2.name_id ); } } inline void WriteHeaderToOutput(std::string & output) { output += "{" "\"version\": 0.3," "\"status\":"; } //TODO: reorder parameters inline void BuildTextualDescription( DescriptionFactory & description_factory, http::Reply & reply, const int route_length, const DataFacadeT * facade, std::vector & route_segments_list ) { //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; 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. 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 += "[\""; if(TurnInstructions.LeaveRoundAbout == current_instruction) { intToString(TurnInstructions.EnterRoundAbout, tmpInstruction); reply.content += tmpInstruction; reply.content += "-"; intToString(roundAbout.leave_at_exit+1, tmpInstruction); reply.content += tmpInstruction; roundAbout.leave_at_exit = 0; } else { intToString(current_instruction, tmpInstruction); reply.content += tmpInstruction; } reply.content += "\",\""; 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 += "]"; route_segments_list.push_back( Segment( segment.nameID, segment.length, route_segments_list.size() ) ); } } else if(TurnInstructions.StayOnRoundAbout == current_instruction) { ++roundAbout.leave_at_exit; } if(segment.necessary) ++prefixSumOfNecessarySegments; } 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_ */