From e7439e92edb742f9cfc8fd7faaada245d651fa11 Mon Sep 17 00:00:00 2001 From: DennisOSRM Date: Thu, 17 Nov 2011 18:04:49 +0100 Subject: [PATCH] Route description are generated --- Contractor/EdgeBasedGraphFactory.cpp | 24 ++-- Contractor/EdgeBasedGraphFactory.h | 4 +- DataStructures/DescriptionFactory.cpp | 112 ++++++++++++++++ DataStructures/DescriptionFactory.h | 176 ++++++++++++++++++++++++++ Plugins/JSONDescriptor.h | 61 ++++++--- Plugins/ViaRoutePlugin.h | 2 - 6 files changed, 348 insertions(+), 31 deletions(-) create mode 100644 DataStructures/DescriptionFactory.cpp create mode 100644 DataStructures/DescriptionFactory.h diff --git a/Contractor/EdgeBasedGraphFactory.cpp b/Contractor/EdgeBasedGraphFactory.cpp index fe967e14e..5dcf146fa 100644 --- a/Contractor/EdgeBasedGraphFactory.cpp +++ b/Contractor/EdgeBasedGraphFactory.cpp @@ -26,7 +26,6 @@ #include #include "EdgeBasedGraphFactory.h" -#include "../DataStructures/ExtractorStructs.h" template<> EdgeBasedGraphFactory::EdgeBasedGraphFactory(int nodes, std::vector & inputEdges, std::vector<_Restriction> & irs, std::vector & nI) @@ -149,15 +148,7 @@ void EdgeBasedGraphFactory::Run() { newEdge.data.backward = false; newEdge.data.via = v; newEdge.data.nameID = _nodeBasedGraph->GetEdgeData(e2).middleName.nameID; - //newEdge.data.nameID2 = _nodeBasedGraph->GetEdgeData(e2).middleName.nameID; - //Todo: turn instruction angeben - - if(newEdge.data.nameID == _nodeBasedGraph->GetEdgeData(e1).middleName.nameID) - newEdge.data.turnInstruction = 0; - else { - //TODO: Winkel berechnen und angepasste Anweisung geben. - newEdge.data.turnInstruction = 1; - } + newEdge.data.turnInstruction = AnalyzeTurn(u, v, w); //create Edge for NearestNeighborlookup edgeBasedEdges.push_back(newEdge); EdgeBasedNode currentNode; @@ -188,6 +179,19 @@ void EdgeBasedGraphFactory::Run() { INFO("Generated " << edgeBasedNodes.size() << " edge based nodes"); } +short EdgeBasedGraphFactory::AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w) const { + _NodeBasedDynamicGraph::EdgeIterator edge1 = _nodeBasedGraph->FindEdge(u, v); + _NodeBasedDynamicGraph::EdgeIterator edge2 = _nodeBasedGraph->FindEdge(v, w); + + _NodeBasedDynamicGraph::EdgeData data1 = _nodeBasedGraph->GetEdgeData(edge1); + _NodeBasedDynamicGraph::EdgeData data2 = _nodeBasedGraph->GetEdgeData(edge2); + + if(data1.middleName.nameID == data2.middleName.nameID) { + return TurnInstructions.NoTurn; + } + return TurnInstructions.GoStraight; +} + unsigned EdgeBasedGraphFactory::GetNumberOfNodes() const { return edgeBasedEdges.size(); } diff --git a/Contractor/EdgeBasedGraphFactory.h b/Contractor/EdgeBasedGraphFactory.h index 3ecbb227f..0ec7d9d5b 100644 --- a/Contractor/EdgeBasedGraphFactory.h +++ b/Contractor/EdgeBasedGraphFactory.h @@ -33,6 +33,7 @@ #include "../DataStructures/ExtractorStructs.h" #include "../DataStructures/ImportEdge.h" #include "../DataStructures/Percent.h" +#include "../DataStructures/TurnInstructions.h" class EdgeBasedGraphFactory { private: @@ -56,7 +57,6 @@ private: int distance; unsigned via; unsigned nameID; -// unsigned nameID2; bool forward; bool backward; short turnInstruction; @@ -103,7 +103,7 @@ public: template< class ImportEdgeT > void GetEdgeBasedEdges( std::vector< ImportEdgeT >& edges ); void GetEdgeBasedNodes( std::vector< EdgeBasedNode> & nodes); - + short AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w) const; unsigned GetNumberOfNodes() const; }; diff --git a/DataStructures/DescriptionFactory.cpp b/DataStructures/DescriptionFactory.cpp new file mode 100644 index 000000000..2c86486eb --- /dev/null +++ b/DataStructures/DescriptionFactory.cpp @@ -0,0 +1,112 @@ +/* + 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. + */ + +#include + +#include "../typedefs.h" +#include "DescriptionFactory.h" + +DescriptionFactory::DescriptionFactory() { } + +DescriptionFactory::~DescriptionFactory() { } + +double DescriptionFactory::GetAngleBetweenCoordinates() const { + return 0.;//GetAngleBetweenTwoEdges(previousCoordinate, currentCoordinate, nextCoordinate); +} + +void DescriptionFactory::SetStartSegment(const PhantomNode & _startPhantom) { + startPhantom = _startPhantom; + AppendSegment(_startPhantom.location, _PathData(0, _startPhantom.nodeBasedEdgeNameID, 10, _startPhantom.weight1)); +} + +void DescriptionFactory::SetEndSegment(const PhantomNode & _targetPhantom) { + targetPhantom = _targetPhantom; + AppendSegment(_targetPhantom.location, _PathData(0, _targetPhantom.nodeBasedEdgeNameID, 0, _targetPhantom.weight1)); +} + +void DescriptionFactory::AppendSegment(const _Coordinate & coordinate, const _PathData & data ) { + //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 +// (_Coordinate & loc, NodeID nam, unsigned len, unsigned dur, short tInstr) + + //Is a new instruction necessary? + //yes: data.turnInstruction != 0; + //no: data.turnInstruction == 0; + pathDescription.push_back(SegmentInformation(coordinate, data.nameID, 0, data.durationOfSegment, data.turnInstruction) ); + +} + +void DescriptionFactory::AppendRouteInstructionString(std::string & output) { + output += "[\"Turn left\",\"High Street\",200,0,10,\"200m\",\"NE\",22.5]"; +} + +void DescriptionFactory::AppendEncodedPolylineString(std::string & output, bool isEncoded) { + if(isEncoded) + pc.printEncodedString(pathDescription, output); + else + pc.printUnencodedString(pathDescription, output); +} + +void DescriptionFactory::AppendEncodedPolylineString(std::string &output) { + pc.printEncodedString(pathDescription, output); +} + +void DescriptionFactory::AppendUnencodedPolylineString(std::string &output) { + pc.printUnencodedString(pathDescription, output); +} + +unsigned DescriptionFactory::Run() { + unsigned entireLength = 0; + /** starts at index 1 */ + pathDescription[0].length = 0; + for(unsigned i = 1; i < pathDescription.size(); ++i) { + pathDescription[i].length = ApproximateDistance(pathDescription[i-1].location, pathDescription[i].location); + } + + unsigned lengthOfSegment = 0; + unsigned durationOfSegment = 0; + unsigned indexOfSegmentBegin = 0; + + for(unsigned i = 1; i < pathDescription.size(); ++i) { + entireLength += pathDescription[i].length; + lengthOfSegment += pathDescription[i].length; + durationOfSegment += pathDescription[i].duration; + pathDescription[indexOfSegmentBegin].length = lengthOfSegment; + pathDescription[indexOfSegmentBegin].duration = durationOfSegment; + if(pathDescription[i].turnInstruction != 0) { + //INFO("Turn after " << lengthOfSegment << "m into way with name id " << segment.nameID); + assert(pathDescription[i].necessary); + lengthOfSegment = 0; + durationOfSegment = 0; + indexOfSegmentBegin = i; + } + } + + //Generalize poly line + BOOST_FOREACH(SegmentInformation & segment, pathDescription) { + //TODO: Replace me by real generalization + segment.necessary = true; + } + + //fix what needs to be fixed else + return entireLength; +} diff --git a/DataStructures/DescriptionFactory.h b/DataStructures/DescriptionFactory.h new file mode 100644 index 000000000..2d56c6dc7 --- /dev/null +++ b/DataStructures/DescriptionFactory.h @@ -0,0 +1,176 @@ +/* + 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 DESCRIPTIONFACTORY_H_ +#define DESCRIPTIONFACTORY_H_ + +#include + +#include "../Algorithms/PolylineCompressor.h" +#include "ExtractorStructs.h" +#include "SegmentInformation.h" + +/* This class is fed with all way segments in consecutive order + * and produces the description plus the encoded polyline */ + +class DescriptionFactory { + PolylineCompressor pc; + PhantomNode startPhantom, targetPhantom; +public: + //I know, declaring this public is considered bad. I'm lazy + std::vector pathDescription; + DescriptionFactory(); + virtual ~DescriptionFactory(); + double GetAngleBetweenCoordinates() const; + void AppendEncodedPolylineString(std::string &output); + void AppendUnencodedPolylineString(std::string &output); + void AppendSegment(const _Coordinate & coordinate, const _PathData & data); + void AppendRouteInstructionString(std::string & output); + void SetStartSegment(const PhantomNode & startPhantom); + void SetEndSegment(const PhantomNode & startPhantom); + void AppendEncodedPolylineString(std::string & output, bool isEncoded); + unsigned Run(); + +// static inline void getDirectionOfInstruction(double angle, DirectionOfInstruction & dirInst) { +// if(angle >= 23 && angle < 67) { +// dirInst.direction = "southeast"; +// dirInst.shortDirection = "SE"; +// return; +// } +// if(angle >= 67 && angle < 113) { +// dirInst.direction = "south"; +// dirInst.shortDirection = "S"; +// return; +// } +// if(angle >= 113 && angle < 158) { +// dirInst.direction = "southwest"; +// dirInst.shortDirection = "SW"; +// return; +// } +// if(angle >= 158 && angle < 202) { +// dirInst.direction = "west"; +// dirInst.shortDirection = "W"; +// return; +// } +// if(angle >= 202 && angle < 248) { +// dirInst.direction = "northwest"; +// dirInst.shortDirection = "NW"; +// return; +// } +// if(angle >= 248 && angle < 292) { +// dirInst.direction = "north"; +// dirInst.shortDirection = "N"; +// return; +// } +// if(angle >= 292 && angle < 336) { +// dirInst.direction = "northeast"; +// dirInst.shortDirection = "NE"; +// return; +// } +// dirInst.direction = "East"; +// dirInst.shortDirection = "E"; +// return; +// } +// +// static inline void getTurnDirectionOfInstruction(double angle, std::string & output) { +// if(angle >= 23 && angle < 67) { +// output = "Turn sharp right"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// if (angle >= 67 && angle < 113) { +// output = "Turn right"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// if (angle >= 113 && angle < 158) { +// output = "Bear right"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// +// if (angle >= 158 && angle < 202) { +// output = "Continue"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// if (angle >= 202 && angle < 248) { +// output = "Bear left"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// if (angle >= 248 && angle < 292) { +// output = "Turn left"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// if (angle >= 292 && angle < 336) { +// output = "Turn sharp left"; +// // cout << "angle " << angle << "-> " << output << endl; +// return; +// } +// output = "U-Turn"; +// // cout << "angle " << angle << "-> " << output << endl; +// } +//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(descriptionFactory.startIndexOfGeometry, tmp); +// output += tmp; +// output += ","; +// intToString(descriptionFactory.durationOfInstruction, tmp); +// output += tmp; +// output += ","; +// output += "\""; +// output += tmpDistance; +// output += "\","; +// double angle = descriptionFactory.GetAngleBetweenCoordinates(); +// DirectionOfInstruction direction; +// getDirectionOfInstruction(angle, direction); +// output += "\""; +// output += direction.shortDirection; +// output += "\","; +// std::stringstream numberString; +// numberString << fixed << setprecision(2) << angle; +// output += numberString.str(); +// } +// output += "]"; +// } +}; + +#endif /* DESCRIPTIONFACTORY_H_ */ diff --git a/Plugins/JSONDescriptor.h b/Plugins/JSONDescriptor.h index f3214291f..84e346f08 100644 --- a/Plugins/JSONDescriptor.h +++ b/Plugins/JSONDescriptor.h @@ -24,7 +24,9 @@ or see http://www.gnu.org/licenses/agpl.txt. #include #include "BaseDescriptor.h" -#include "../DataStructures/JSONDescriptionFactory.h" +#include "../DataStructures/DescriptionFactory.h" +#include "../DataStructures/SegmentInformation.h" +#include "../DataStructures/TurnInstructions.h" #include "../Util/StringUtil.h" template @@ -32,7 +34,7 @@ class JSONDescriptor : public BaseDescriptor{ private: _DescriptorConfig config; _RouteSummary summary; - JSONDescriptionFactory descriptionFactory; + DescriptionFactory descriptionFactory; std::string tmp; _Coordinate current; @@ -44,33 +46,29 @@ public: WriteHeaderToOutput(reply.content); //We do not need to do much, if there is no route ;-) - //INFO("Starting at " << sEngine.GetEscapedNameForNameID(phantomNodes.startPhantom.nodeBasedEdgeNameID) << ", id: " << phantomNodes.startPhantom.nodeBasedEdgeNameID); - //INFO("Arriving at " << sEngine.GetEscapedNameForNameID(phantomNodes.targetPhantom.nodeBasedEdgeNameID) << ", id: " << phantomNodes.startPhantom.nodeBasedEdgeNameID); - if(durationOfTrip != INT_MAX && rawRoute.routeSegments.size() > 0) { summary.startName = sEngine.GetEscapedNameForNameID(phantomNodes.startPhantom.nodeBasedEdgeNameID); + descriptionFactory.SetStartSegment(phantomNodes.startPhantom); summary.destName = sEngine.GetEscapedNameForNameID(phantomNodes.targetPhantom.nodeBasedEdgeNameID); - summary.BuildDurationAndLengthStrings(0, durationOfTrip); reply.content += "0," "\"status_message\": \"Found route between points\","; - descriptionFactory.AddToPolyline(phantomNodes.startPhantom.location); for(unsigned segmentIdx = 0; segmentIdx < rawRoute.routeSegments.size(); segmentIdx++) { const std::vector< _PathData > & path = rawRoute.routeSegments[segmentIdx]; BOOST_FOREACH(_PathData pathData, path) { sEngine.GetCoordinatesForNodeID(pathData.node, current); - descriptionFactory.AppendSegment(pathData, current); - if(pathData.turnInstruction != 0) { - INFO("Turn on " << sEngine.GetEscapedNameForNameID(pathData.nameID) << ", turnID: " << pathData.turnInstruction ); - } + descriptionFactory.AppendSegment(current, pathData ); } + //TODO: Add via points } - descriptionFactory.AddToPolyline(phantomNodes.targetPhantom.location); + descriptionFactory.SetEndSegment(phantomNodes.targetPhantom); } else { //no route found reply.content += "207," "\"status_message\": \"Cannot find route between points\","; } + summary.BuildDurationAndLengthStrings(descriptionFactory.Run(), durationOfTrip); + reply.content += "\"route_summary\": {" "\"total_distance\":"; reply.content += summary.lengthString; @@ -88,17 +86,46 @@ public: reply.content += "\"route_geometry\": "; if(config.geometry) { if(config.encodeGeometry) - descriptionFactory.AppendEncodedPolylineString(reply.content); - else - descriptionFactory.AppendUnencodedPolylineString(reply.content); + descriptionFactory.AppendEncodedPolylineString(reply.content, config.encodeGeometry); } else { reply.content += "[]"; } reply.content += "," "\"route_instructions\": ["; - if(config.instructions) - descriptionFactory.AppendRouteInstructionString(reply.content); + if(config.instructions) { + unsigned prefixSumOfNecessarySegments = 0; + std::string tmpDist, tmpLength, tmp; + //Fetch data from Factory and generate a string from it. + BOOST_FOREACH(SegmentInformation segment, descriptionFactory.pathDescription) { + //["instruction","streetname",length,position,time,"length","earth_direction",azimuth] + if(0 != segment.turnInstruction) { + if(0 != prefixSumOfNecessarySegments) + reply.content += ","; + reply.content += "[\""; + reply.content += TurnInstructions.TurnStrings[segment.turnInstruction]; + reply.content += "\",\""; + reply.content += sEngine.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, tmp); + reply.content += ",\""; + reply.content += tmpLength; + //TODO: fix heading + reply.content += "\",\"NE\",22.5"; + reply.content += "]"; + } + if(segment.necessary) + ++prefixSumOfNecessarySegments; + } + // descriptionFactory.AppendRouteInstructionString(reply.content); + + } reply.content += "],"; //list all viapoints so that the client may display it reply.content += "\"via_points\":["; diff --git a/Plugins/ViaRoutePlugin.h b/Plugins/ViaRoutePlugin.h index 3bbee20df..638d29db2 100644 --- a/Plugins/ViaRoutePlugin.h +++ b/Plugins/ViaRoutePlugin.h @@ -171,8 +171,6 @@ public: rawRoute.routeSegments[i] = path; } } - INFO("Found path of length " << distance); - reply.status = http::Reply::ok; BaseDescriptor > > * desc;