324 lines
12 KiB
C++
324 lines
12 KiB
C++
#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 "util/integer_range.hpp"
|
|
#include "util/coordinate_calculation.hpp"
|
|
#include "util/container.hpp"
|
|
#include "extractor/turn_instructions.hpp"
|
|
|
|
#include <boost/assert.hpp>
|
|
|
|
#include <cmath>
|
|
#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 (!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 (!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, extractor::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 extractor::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 ? extractor::TurnInstruction::ReachViaLocation
|
|
: extractor::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()) && (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 extractor::TravelMode previous_mode)
|
|
{
|
|
if (extractor::TurnInstruction::NoTurn == path_point.turn_instruction &&
|
|
previous_mode != path_point.travel_mode && path_point.segment_duration > 0)
|
|
{
|
|
return extractor::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 : util::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 = util::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 : util::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 (extractor::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.));
|
|
|
|
// 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 = extractor::TurnInstruction::NoTurn;
|
|
}
|
|
|
|
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 = extractor::TurnInstruction::HeadOn;
|
|
segments.front().necessary = true;
|
|
}
|
|
|
|
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 =
|
|
util::coordinate_calculation::bearing(first.location, second.location);
|
|
const double pre_turn_bearing =
|
|
util::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
|
|
util::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_
|