osrm-backend/Plugins/JSONDescriptor.h
2011-09-28 17:22:03 +02:00

292 lines
17 KiB
C++

/*
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 "../DataStructures/PolylineCompressor.h"
template<class SearchEngineT>
class JSONDescriptor : public BaseDescriptor<SearchEngineT>{
private:
_DescriptorConfig config;
RouteSummary summary;
DirectionOfInstruction directionOfInstruction;
DescriptorState descriptorState;
std::string tmp;
vector<_Coordinate> polyline;
public:
JSONDescriptor() {}
void SetConfig(const _DescriptorConfig & c) { config = c; }
void Run(http::Reply & reply, RawRouteData &rawRoute, PhantomNodes &phantomNodes, SearchEngineT &sEngine, unsigned distance) {
WriteHeaderToOutput(reply.content);
//We do not need to do much, if there is no route ;-)
if(distance != UINT_MAX && rawRoute.routeSegments.size() > 0) {
reply.content += "0,"
"\"status_message\": \"Found route between points\",";
//Put first segment of route into geometry
polyline.push_back(phantomNodes.startPhantom.location);
descriptorState.geometryCounter++;
descriptorState.startOfSegmentCoordinate = phantomNodes.startPhantom.location;
//Generate initial instruction for start of route (order of NodeIDs does not matter, its the same name anyway)
summary.startName = sEngine.GetEscapedNameForOriginDestinationNodeID(phantomNodes.startPhantom.startNode, phantomNodes.startPhantom.targetNode);
descriptorState.lastNameID = sEngine.GetNameIDForOriginDestinationNodeID(phantomNodes.startPhantom.startNode, phantomNodes.startPhantom.targetNode);
//If we have a route, i.e. start and dest not on same edge, than get it
if(rawRoute.routeSegments[0].size() > 0)
sEngine.getCoordinatesForNodeID(rawRoute.routeSegments[0].begin()->node, descriptorState.tmpCoord);
else
descriptorState.tmpCoord = phantomNodes.targetPhantom.location;
descriptorState.previousCoordinate = phantomNodes.startPhantom.location;
descriptorState.currentCoordinate = descriptorState.tmpCoord;
descriptorState.distanceOfInstruction += ApproximateDistance(descriptorState.previousCoordinate, descriptorState.currentCoordinate);
if(config.instructions) {
//Get Heading
double angle = GetAngleBetweenTwoEdges(_Coordinate(phantomNodes.startPhantom.location.lat, phantomNodes.startPhantom.location.lon), descriptorState.tmpCoord, _Coordinate(descriptorState.tmpCoord.lat, descriptorState.tmpCoord.lon-1000));
getDirectionOfInstruction(angle, directionOfInstruction);
appendInstructionNameToString(summary.startName, directionOfInstruction.direction, descriptorState.routeInstructionString, true);
}
NodeID lastNodeID = UINT_MAX;
for(unsigned segmentIdx = 0; segmentIdx < rawRoute.routeSegments.size(); segmentIdx++) {
const std::vector< _PathData > & path = rawRoute.routeSegments[segmentIdx];
if(path.empty())
continue;
if ( UINT_MAX == lastNodeID) {
lastNodeID = (phantomNodes.startPhantom.startNode == (*path.begin()).node ? phantomNodes.startPhantom.targetNode : phantomNodes.startPhantom.startNode);
}
//Check, if there is overlap between current and previous route segment
//if not, than we are fine and can route over this edge without paying any special attention.
if(lastNodeID == (*path.begin()).node) {
// appendCoordinateToString(descriptorState.currentCoordinate, descriptorState.routeGeometryString);
polyline.push_back(descriptorState.currentCoordinate);
descriptorState.geometryCounter++;
lastNodeID = (lastNodeID == rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.startNode ? rawRoute.segmentEndCoordinates[segmentIdx].targetPhantom.startNode : rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.startNode);
//output of the via nodes coordinates
polyline.push_back(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location);
descriptorState.geometryCounter++;
descriptorState.currentNameID = sEngine.GetNameIDForOriginDestinationNodeID(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.startNode, rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.targetNode);
//Make a special announement to do a U-Turn.
appendInstructionLengthToString(descriptorState.distanceOfInstruction, descriptorState.routeInstructionString);
descriptorState.routeInstructionString += ",";
descriptorState.distanceOfInstruction = ApproximateDistance(descriptorState.currentCoordinate, rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location);
getTurnDirectionOfInstruction(descriptorState.GetAngleBetweenCoordinates(), tmp);
appendInstructionNameToString(sEngine.GetEscapedNameForNameID(descriptorState.currentNameID), tmp, descriptorState.routeInstructionString);
appendInstructionLengthToString(descriptorState.distanceOfInstruction, descriptorState.routeInstructionString);
descriptorState.routeInstructionString += ",";
tmp = "U-turn at via point";
appendInstructionNameToString(sEngine.GetEscapedNameForNameID(descriptorState.currentNameID), tmp, descriptorState.routeInstructionString);
double tmpDistance = descriptorState.distanceOfInstruction;
descriptorState.SetStartOfSegment(); //Set start of segment but save distance information.
descriptorState.distanceOfInstruction = tmpDistance;
} else if(segmentIdx > 0) { //We are going straight through an edge which is carrying the via point.
assert(segmentIdx != 0);
//routeInstructionString += "reaching via node: ";
descriptorState.nextCoordinate = rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location;
descriptorState.currentNameID = sEngine.GetNameIDForOriginDestinationNodeID(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.startNode, rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.targetNode);
polyline.push_back(descriptorState.currentCoordinate);
descriptorState.geometryCounter++;
polyline.push_back(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location);
descriptorState.geometryCounter++;
if(config.instructions) {
double turnAngle = descriptorState.GetAngleBetweenCoordinates();
appendInstructionLengthToString(descriptorState.distanceOfInstruction, descriptorState.routeInstructionString);
descriptorState.SetStartOfSegment();
descriptorState.routeInstructionString += ",";
getTurnDirectionOfInstruction(turnAngle, tmp);
tmp += " and reach via point";
appendInstructionNameToString(sEngine.GetEscapedNameForNameID(descriptorState.currentNameID), tmp, descriptorState.routeInstructionString);
//instruction to continue on the segment
appendInstructionLengthToString(ApproximateDistance(descriptorState.currentCoordinate, descriptorState.nextCoordinate), descriptorState.routeInstructionString);
descriptorState.entireDistance += ApproximateDistance(descriptorState.currentCoordinate, descriptorState.nextCoordinate);
descriptorState.routeInstructionString += ",";
appendInstructionNameToString(sEngine.GetEscapedNameForNameID(descriptorState.currentNameID), "Continue ", descriptorState.routeInstructionString);
//note the new segment starting coordinates
descriptorState.SetStartOfSegment();
descriptorState.previousCoordinate = descriptorState.currentCoordinate;
descriptorState.currentCoordinate = descriptorState.nextCoordinate;
}
}
for(vector< _PathData >::const_iterator it = path.begin(); it != path.end(); it++) {
sEngine.getCoordinatesForNodeID(it->node, descriptorState.nextCoordinate);
descriptorState.currentNameID = sEngine.GetNameIDForOriginDestinationNodeID(lastNodeID, it->node);
double area = fabs(0.5*( descriptorState.startOfSegmentCoordinate.lon*(descriptorState.nextCoordinate.lat - descriptorState.currentCoordinate.lat) + descriptorState.nextCoordinate.lon*(descriptorState.currentCoordinate.lat - descriptorState.startOfSegmentCoordinate.lat) + descriptorState.currentCoordinate.lon*(descriptorState.startOfSegmentCoordinate.lat - descriptorState.nextCoordinate.lat) ) );
//if route is generalization does not skip this point, add it to description
if( config.z == 19 || area >= areaThresholds[config.z] || (false == descriptorState.CurrentAndPreviousNameIDsEqual()) ) {
//mark the beginning of the segment thats announced
// appendCoordinateToString(descriptorState.currentCoordinate, descriptorState.routeGeometryString);
polyline.push_back(descriptorState.currentCoordinate);
descriptorState.geometryCounter++;
if( ( false == descriptorState.CurrentAndPreviousNameIDsEqual() ) && config.instructions) {
appendInstructionLengthToString(descriptorState.distanceOfInstruction, descriptorState.routeInstructionString);
descriptorState.routeInstructionString += ",";
getTurnDirectionOfInstruction(descriptorState.GetAngleBetweenCoordinates(), tmp);
appendInstructionNameToString(sEngine.GetEscapedNameForNameID(descriptorState.currentNameID), tmp, descriptorState.routeInstructionString);
//note the new segment starting coordinates
descriptorState.SetStartOfSegment();
}
}
descriptorState.distanceOfInstruction += ApproximateDistance(descriptorState.currentCoordinate, descriptorState.nextCoordinate);
lastNodeID = it->node;
if(it != path.begin()) {
descriptorState.previousCoordinate = descriptorState.currentCoordinate;
descriptorState.currentCoordinate = descriptorState.nextCoordinate;
}
}
}
descriptorState.currentNameID = sEngine.GetNameIDForOriginDestinationNodeID(phantomNodes.targetPhantom.startNode, phantomNodes.targetPhantom.targetNode);
descriptorState.nextCoordinate = phantomNodes.targetPhantom.location;
polyline.push_back(descriptorState.currentCoordinate);
descriptorState.geometryCounter++;
if((false == descriptorState.CurrentAndPreviousNameIDsEqual()) && config.instructions) {
appendInstructionLengthToString(descriptorState.distanceOfInstruction, descriptorState.routeInstructionString);
descriptorState.routeInstructionString += ",";
getTurnDirectionOfInstruction(descriptorState.GetAngleBetweenCoordinates(), tmp);
appendInstructionNameToString(sEngine.GetEscapedNameForNameID(descriptorState.currentNameID), tmp, descriptorState.routeInstructionString);
descriptorState.distanceOfInstruction = 0;
descriptorState.SetStartOfSegment();
}
summary.destName = sEngine.GetEscapedNameForNameID(descriptorState.currentNameID);
descriptorState.distanceOfInstruction += ApproximateDistance(descriptorState.currentCoordinate, descriptorState.nextCoordinate);
polyline.push_back(phantomNodes.targetPhantom.location);
descriptorState.geometryCounter++;
appendInstructionLengthToString(descriptorState.distanceOfInstruction, descriptorState.routeInstructionString);
summary.BuildDurationAndLengthStrings(descriptorState.entireDistance, distance);
} else {
//no route found
reply.content += "207,"
"\"status_message\": \"Cannot find route between points\",";
}
reply.content += "\"route_summary\": {"
"\"total_distance\":";
reply.content += summary.lengthString;
reply.content += ","
"\"total_time\":";
reply.content += summary.durationString;
reply.content += ","
"\"start_point\":\"";
reply.content += summary.startName;
reply.content += "\","
"\"end_point\":\"";
reply.content += summary.destName;
reply.content += "\"";
reply.content += "},";
reply.content += "\"route_geometry\": ";
if(config.geometry) {
if(config.encodeGeometry)
config.pc.printEncodedString(polyline, descriptorState.routeGeometryString);
else
config.pc.printUnencodedString(polyline, descriptorState.routeGeometryString);
reply.content += descriptorState.routeGeometryString;
} else {
reply.content += "[]";
}
reply.content += ","
"\"route_instructions\": [";
if(config.instructions)
reply.content += descriptorState.routeInstructionString;
reply.content += "],";
//list all viapoints so that the client may display it
reply.content += "\"via_points\":[";
for(unsigned segmentIdx = 1; (true == config.geometry) && (segmentIdx < rawRoute.segmentEndCoordinates.size()); segmentIdx++) {
if(segmentIdx > 1)
reply.content += ",";
reply.content += "[";
if(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location.isSet())
convertInternalReversedCoordinateToString(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location, tmp);
else
convertInternalReversedCoordinateToString(rawRoute.rawViaNodeCoordinates[segmentIdx], tmp);
reply.content += tmp;
reply.content += "]";
}
reply.content += "],"
"\"transactionId\": \"OSRM Routing Engine JSON Descriptor (v0.2)\"";
reply.content += "}";
}
private:
void appendInstructionNameToString(const std::string & nameOfStreet, const std::string & instructionOrDirection, std::string &output, bool firstAdvice = false) {
output += "[";
if(config.instructions) {
output += "\"";
if(firstAdvice) {
output += "Head ";
}
output += instructionOrDirection;
output += "\",\"";
output += nameOfStreet;
output += "\",";
}
}
void appendInstructionLengthToString(unsigned length, std::string &output) {
if(config.instructions){
std::string tmpDistance;
intToString(10*(round(length/10.)), tmpDistance);
output += tmpDistance;
output += ",";
intToString(descriptorState.startIndexOfGeometry, tmp);
output += tmp;
output += ",";
intToString(descriptorState.durationOfInstruction, tmp);
output += tmp;
output += ",";
output += "\"";
output += tmpDistance;
output += "\",";
double angle = descriptorState.GetAngleBetweenCoordinates();
DirectionOfInstruction direction;
getDirectionOfInstruction(angle, direction);
output += "\"";
output += direction.shortDirection;
output += "\",";
std::stringstream numberString;
numberString << fixed << setprecision(2) << angle;
output += numberString.str();
}
output += "]";
}
void WriteHeaderToOutput(std::string & output) {
output += "{"
"\"version\": 0.3,"
"\"status\":";
}
};
#endif /* JSON_DESCRIPTOR_H_ */