Refactor guidance generation
This commit is contained in:
committed by
Patrick Niklaus
parent
fa4ba42f15
commit
efd33b295a
@@ -0,0 +1,66 @@
|
||||
#ifndef ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_
|
||||
#define ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_
|
||||
|
||||
#include "engine/segment_inforamtion.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
/*
|
||||
Simplify turn instructions
|
||||
Input :
|
||||
10. Turn left on B 36 for 20 km
|
||||
11. Continue on B 35; B 36 for 2 km
|
||||
12. Continue on B 36 for 13 km
|
||||
|
||||
Output:
|
||||
10. Turn left on B 36 for 35 km
|
||||
*/
|
||||
|
||||
inline void CombineSimilarSegments(std::vector<SegmentInformation> &segments)
|
||||
{
|
||||
// TODO: rework to check only end and start of string.
|
||||
// stl string is way to expensive
|
||||
// unsigned lastTurn = 0;
|
||||
// for(unsigned i = 1; i < path_description.size(); ++i) {
|
||||
// string1 = sEngine.GetEscapedNameForNameID(path_description[i].name_id);
|
||||
// if(TurnInstruction::GoStraight == path_description[i].turn_instruction) {
|
||||
// if(std::string::npos != string0.find(string1+";")
|
||||
// || std::string::npos != string0.find(";"+string1)
|
||||
// || std::string::npos != string0.find(string1+" ;")
|
||||
// || std::string::npos != string0.find("; "+string1)
|
||||
// ){
|
||||
// SimpleLogger().Write() << "->next correct: " << string0 << " contains " <<
|
||||
// string1;
|
||||
// for(; lastTurn != i; ++lastTurn)
|
||||
// path_description[lastTurn].name_id = path_description[i].name_id;
|
||||
// path_description[i].turn_instruction = TurnInstruction::NoTurn;
|
||||
// } else if(std::string::npos != string1.find(string0+";")
|
||||
// || std::string::npos != string1.find(";"+string0)
|
||||
// || std::string::npos != string1.find(string0+" ;")
|
||||
// || std::string::npos != string1.find("; "+string0)
|
||||
// ){
|
||||
// SimpleLogger().Write() << "->prev correct: " << string1 << " contains " <<
|
||||
// string0;
|
||||
// path_description[i].name_id = path_description[i-1].name_id;
|
||||
// path_description[i].turn_instruction = TurnInstruction::NoTurn;
|
||||
// }
|
||||
// }
|
||||
// if (TurnInstruction::NoTurn != path_description[i].turn_instruction) {
|
||||
// lastTurn = i;
|
||||
// }
|
||||
// string0 = string1;
|
||||
// }
|
||||
//
|
||||
}
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif //ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_
|
||||
@@ -0,0 +1,326 @@
|
||||
#ifndef ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
#define ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
#include "engine/douglas_peucker.hpp"
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "engine/segment_information.hpp"
|
||||
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
// transfers the internal edge based data structures to a more useable format
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
template <typename DataFacadeT> class SegmentList
|
||||
{
|
||||
public:
|
||||
using DataFacade = DataFacadeT;
|
||||
SegmentList(const InternalRouteResult &raw_route,
|
||||
const bool extract_alternative,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification,
|
||||
const DataFacade *facade);
|
||||
|
||||
const std::vector<std::uint32_t> &GetViaIndices() const;
|
||||
std::uint32_t GetDistance() const;
|
||||
std::uint32_t GetDuration() const;
|
||||
|
||||
const std::vector<SegmentInformation> &Get() const;
|
||||
|
||||
private:
|
||||
void InitRoute(const PhantomNode &phantom_node, const bool traversed_in_reverse);
|
||||
void AddLeg(const std::vector<PathData> &leg_data,
|
||||
const PhantomNode &target_node,
|
||||
const bool traversed_in_reverse,
|
||||
const bool is_via_leg,
|
||||
const DataFacade *facade);
|
||||
|
||||
void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &path_point);
|
||||
void Finalize(const bool extract_alternative,
|
||||
const InternalRouteResult &raw_route,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification);
|
||||
|
||||
// journey length in tenth of a second
|
||||
std::uint32_t total_distance;
|
||||
// journey distance in meter (TODO: verify)
|
||||
std::uint32_t total_duration;
|
||||
|
||||
// segments that are required to keep
|
||||
std::vector<std::uint32_t> via_indices;
|
||||
|
||||
// a list of node based segments
|
||||
std::vector<SegmentInformation> segments;
|
||||
};
|
||||
|
||||
template <typename DataFacadeT>
|
||||
SegmentList<DataFacadeT>::SegmentList(const InternalRouteResult &raw_route,
|
||||
const bool extract_alternative,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification,
|
||||
const DataFacade *facade)
|
||||
: total_distance(0), total_duration(0)
|
||||
{
|
||||
if (not raw_route.is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (extract_alternative)
|
||||
{
|
||||
BOOST_ASSERT(raw_route.has_alternative());
|
||||
InitRoute(raw_route.segment_end_coordinates.front().source_phantom,
|
||||
raw_route.alt_source_traversed_in_reverse.front());
|
||||
AddLeg(raw_route.unpacked_alternative,
|
||||
raw_route.segment_end_coordinates.back().target_phantom,
|
||||
raw_route.alt_source_traversed_in_reverse.back(), false, facade);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitRoute(raw_route.segment_end_coordinates.front().source_phantom,
|
||||
raw_route.source_traversed_in_reverse.front());
|
||||
for (std::size_t raw_index = 0; raw_index < raw_route.segment_end_coordinates.size();
|
||||
++raw_index)
|
||||
{
|
||||
AddLeg(raw_route.unpacked_path_segments[raw_index],
|
||||
raw_route.segment_end_coordinates[raw_index].target_phantom,
|
||||
raw_route.target_traversed_in_reverse[raw_index],
|
||||
raw_route.is_via_leg(raw_index), facade);
|
||||
}
|
||||
}
|
||||
|
||||
if (not allow_simplification)
|
||||
{
|
||||
// to prevent any simplifications, we mark all segments as necessary
|
||||
for (auto &segment : segments)
|
||||
{
|
||||
segment.necessary = true;
|
||||
}
|
||||
}
|
||||
|
||||
Finalize(extract_alternative, raw_route, zoom_level, allow_simplification);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::InitRoute(const PhantomNode &node, const bool traversed_in_reverse)
|
||||
{
|
||||
const auto segment_duration =
|
||||
(traversed_in_reverse ? node.reverse_weight : node.forward_weight);
|
||||
const auto travel_mode =
|
||||
(traversed_in_reverse ? node.backward_travel_mode : node.forward_travel_mode);
|
||||
|
||||
AppendSegment(node.location, PathData(0, node.name_id, TurnInstruction::HeadOn,
|
||||
segment_duration, travel_mode));
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::AddLeg(const std::vector<PathData> &leg_data,
|
||||
const PhantomNode &target_node,
|
||||
const bool traversed_in_reverse,
|
||||
const bool is_via_leg,
|
||||
const DataFacade *facade)
|
||||
{
|
||||
for (const PathData &path_data : leg_data)
|
||||
{
|
||||
AppendSegment(facade->GetCoordinateOfNode(path_data.node), path_data);
|
||||
}
|
||||
|
||||
const EdgeWeight segment_duration =
|
||||
(traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight);
|
||||
const TravelMode travel_mode =
|
||||
(traversed_in_reverse ? target_node.backward_travel_mode : target_node.forward_travel_mode);
|
||||
segments.emplace_back(target_node.location, target_node.name_id, segment_duration, 0.f,
|
||||
is_via_leg ? TurnInstruction::ReachViaLocation : TurnInstruction::NoTurn,
|
||||
true, true, travel_mode);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT> std::uint32_t SegmentList<DataFacadeT>::GetDistance() const
|
||||
{
|
||||
return total_distance;
|
||||
}
|
||||
template <typename DataFacadeT> std::uint32_t SegmentList<DataFacadeT>::GetDuration() const
|
||||
{
|
||||
return total_duration;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
std::vector<std::uint32_t> const &SegmentList<DataFacadeT>::GetViaIndices() const
|
||||
{
|
||||
return via_indices;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
std::vector<SegmentInformation> const &SegmentList<DataFacadeT>::Get() const
|
||||
{
|
||||
return segments;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::AppendSegment(const FixedPointCoordinate &coordinate,
|
||||
const PathData &path_point)
|
||||
{
|
||||
// if the start location is on top of a node, the first movement might be zero-length,
|
||||
// in which case we dont' add a new description, but instead update the existing one
|
||||
if ((1 == segments.size()) and (segments.front().location == coordinate))
|
||||
{
|
||||
if (path_point.segment_duration > 0)
|
||||
{
|
||||
segments.front().name_id = path_point.name_id;
|
||||
segments.front().travel_mode = path_point.travel_mode;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure mode changes are announced, even when there otherwise is no turn
|
||||
const auto getTurn = [](const PathData &path_point, const TravelMode previous_mode)
|
||||
{
|
||||
if (TurnInstruction::NoTurn == path_point.turn_instruction and
|
||||
previous_mode != path_point.travel_mode and path_point.segment_duration > 0)
|
||||
{
|
||||
return TurnInstruction::GoStraight;
|
||||
}
|
||||
return path_point.turn_instruction;
|
||||
};
|
||||
|
||||
// TODO check why we require .front() here
|
||||
const auto turn = segments.size() ? getTurn(path_point, segments.front().travel_mode)
|
||||
: path_point.turn_instruction;
|
||||
|
||||
segments.emplace_back(coordinate, path_point.name_id, path_point.segment_duration, 0.f, turn,
|
||||
path_point.travel_mode);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::Finalize(const bool extract_alternative,
|
||||
const InternalRouteResult &raw_route,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification)
|
||||
{
|
||||
if (segments.empty())
|
||||
return;
|
||||
|
||||
segments[0].length = 0.f;
|
||||
for (const auto i : osrm::irange<std::size_t>(1, segments.size()))
|
||||
{
|
||||
// move down names by one, q&d hack
|
||||
segments[i - 1].name_id = segments[i].name_id;
|
||||
segments[i].length = coordinate_calculation::greatCircleDistance(segments[i - 1].location,
|
||||
segments[i].location);
|
||||
}
|
||||
|
||||
float segment_length = 0.;
|
||||
EdgeWeight segment_duration = 0;
|
||||
std::size_t segment_start_index = 0;
|
||||
|
||||
double path_length = 0;
|
||||
|
||||
for (const auto i : osrm::irange<std::size_t>(1, segments.size()))
|
||||
{
|
||||
path_length += segments[i].length;
|
||||
segment_length += segments[i].length;
|
||||
segment_duration += segments[i].duration;
|
||||
segments[segment_start_index].length = segment_length;
|
||||
segments[segment_start_index].duration = segment_duration;
|
||||
|
||||
if (TurnInstruction::NoTurn != segments[i].turn_instruction)
|
||||
{
|
||||
BOOST_ASSERT(segments[i].necessary);
|
||||
segment_length = 0;
|
||||
segment_duration = 0;
|
||||
segment_start_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
total_distance = static_cast<std::uint32_t>(std::round(path_length));
|
||||
total_duration = static_cast<std::uint32_t>(std::round(
|
||||
(extract_alternative ? raw_route.alternative_path_length : raw_route.shortest_path_length) /
|
||||
10.));
|
||||
|
||||
auto start_phantom = raw_route.segment_end_coordinates.front().source_phantom;
|
||||
auto target_phantom = raw_route.segment_end_coordinates.back().target_phantom;
|
||||
|
||||
// Post-processing to remove empty or nearly empty path segments
|
||||
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.back().length &&
|
||||
!(segments.end() - 2)->is_via_location)
|
||||
{
|
||||
segments.pop_back();
|
||||
segments.back().necessary = true;
|
||||
segments.back().turn_instruction = TurnInstruction::NoTurn;
|
||||
target_phantom.name_id =
|
||||
(segments.end() - 2)
|
||||
->name_id; // TODO check whether this -2 is desired after the pop-back
|
||||
}
|
||||
|
||||
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.front().length &&
|
||||
!(segments.begin() + 1)->is_via_location)
|
||||
{
|
||||
segments.erase(segments.begin());
|
||||
segments.front().turn_instruction = TurnInstruction::HeadOn;
|
||||
segments.front().necessary = true;
|
||||
start_phantom.name_id = segments.front().name_id;
|
||||
}
|
||||
|
||||
if (allow_simplification)
|
||||
{
|
||||
DouglasPeucker polyline_generalizer;
|
||||
polyline_generalizer.Run(segments.begin(), segments.end(), zoom_level);
|
||||
}
|
||||
|
||||
std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces
|
||||
via_indices.push_back(0);
|
||||
const auto markNecessarySegments = [this, &necessary_segments](SegmentInformation &first,
|
||||
const SegmentInformation &second)
|
||||
{
|
||||
if (!first.necessary)
|
||||
return;
|
||||
|
||||
// mark the end of a leg (of several segments)
|
||||
if (first.is_via_location)
|
||||
via_indices.push_back(necessary_segments);
|
||||
|
||||
const double post_turn_bearing =
|
||||
coordinate_calculation::bearing(first.location, second.location);
|
||||
const double pre_turn_bearing =
|
||||
coordinate_calculation::bearing(second.location, first.location);
|
||||
first.post_turn_bearing = static_cast<short>(post_turn_bearing * 10);
|
||||
first.pre_turn_bearing = static_cast<short>(pre_turn_bearing * 10);
|
||||
|
||||
++necessary_segments;
|
||||
};
|
||||
|
||||
// calculate which segments are necessary and update segments for bearings
|
||||
osrm::for_each_pair(segments, markNecessarySegments);
|
||||
via_indices.push_back(necessary_segments);
|
||||
|
||||
BOOST_ASSERT(via_indices.size() >= 2);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
SegmentList<DataFacadeT> MakeSegmentList(const InternalRouteResult &raw_route,
|
||||
const bool extract_alternative,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification,
|
||||
const DataFacadeT *facade)
|
||||
{
|
||||
return SegmentList<DataFacadeT>(raw_route, extract_alternative, zoom_level,
|
||||
allow_simplification, facade);
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_
|
||||
#define ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_
|
||||
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "engine/guidance/segment_list.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/cast.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
template< typename DataFacadeT >
|
||||
inline osrm::json::Array
|
||||
AnnotateRoute(const std::vector<SegmentInformation> &route_segments, DataFacadeT* facade)
|
||||
{
|
||||
osrm::json::Array json_instruction_array;
|
||||
if( route_segments.empty() )
|
||||
return json_instruction_array;
|
||||
// Segment information has following format:
|
||||
//["instruction id","streetname",length,position,time,"length","earth_direction",azimuth]
|
||||
std::int32_t necessary_segments_running_index = 0;
|
||||
|
||||
struct RoundAbout
|
||||
{
|
||||
std::int32_t start_index;
|
||||
std::uint32_t name_id;
|
||||
std::int32_t leave_at_exit;
|
||||
} round_about;
|
||||
|
||||
round_about = {std::numeric_limits<std::int32_t>::max(), 0, 0};
|
||||
std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction;
|
||||
|
||||
//Generate annotations for every segment
|
||||
for (const SegmentInformation &segment : route_segments)
|
||||
{
|
||||
osrm::json::Array json_instruction_row;
|
||||
TurnInstruction 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 =
|
||||
std::to_string(cast::enum_to_underlying(TurnInstruction::EnterRoundAbout));
|
||||
current_turn_instruction += temp_instruction;
|
||||
current_turn_instruction += "-";
|
||||
temp_instruction = std::to_string(round_about.leave_at_exit + 1);
|
||||
current_turn_instruction += temp_instruction;
|
||||
round_about.leave_at_exit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_instruction =
|
||||
std::to_string(cast::enum_to_underlying(current_instruction));
|
||||
current_turn_instruction += temp_instruction;
|
||||
}
|
||||
json_instruction_row.values.emplace_back(std::move(current_turn_instruction));
|
||||
|
||||
json_instruction_row.values.push_back(facade->get_name_for_id(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(std::round(segment.duration / 10.));
|
||||
json_instruction_row.values.push_back(
|
||||
std::to_string(static_cast<std::uint32_t>(segment.length)) + "m");
|
||||
|
||||
// post turn bearing
|
||||
const double post_turn_bearing_value = (segment.post_turn_bearing / 10.);
|
||||
json_instruction_row.values.push_back(bearing::get(post_turn_bearing_value));
|
||||
json_instruction_row.values.push_back(
|
||||
static_cast<std::uint32_t>(std::round(post_turn_bearing_value)));
|
||||
|
||||
json_instruction_row.values.push_back(segment.travel_mode);
|
||||
|
||||
// pre turn bearing
|
||||
const double pre_turn_bearing_value = (segment.pre_turn_bearing / 10.);
|
||||
json_instruction_row.values.push_back(bearing::get(pre_turn_bearing_value));
|
||||
json_instruction_row.values.push_back(
|
||||
static_cast<std::uint32_t>(std::round(pre_turn_bearing_value)));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
osrm::json::Array json_last_instruction_row;
|
||||
temp_instruction =
|
||||
std::to_string(cast::enum_to_underlying(TurnInstruction::ReachedYourDestination));
|
||||
json_last_instruction_row.values.emplace_back( std::move(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(bearing::get(0.0));
|
||||
json_last_instruction_row.values.push_back(0.);
|
||||
json_last_instruction_row.values.push_back(bearing::get(0.0));
|
||||
json_last_instruction_row.values.push_back(0.);
|
||||
json_instruction_array.values.emplace_back(std::move(json_last_instruction_row));
|
||||
|
||||
return json_instruction_array;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user