osrm-backend/include/engine/guidance/segment_list.hpp

324 lines
12 KiB
C++
Raw Normal View History

2016-01-04 04:19:25 -05:00
#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"
2016-01-05 10:51:13 -05:00
#include "util/container.hpp"
2016-01-04 04:19:25 -05:00
#include "extractor/turn_instructions.hpp"
#include <boost/assert.hpp>
#include <cmath>
2016-01-04 04:19:25 -05:00
#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)
{
2016-01-08 06:43:33 -05:00
if (!raw_route.is_valid())
2016-01-04 04:19:25 -05:00
{
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);
}
}
2016-01-08 06:43:33 -05:00
if (!allow_simplification)
2016-01-04 04:19:25 -05:00
{
// 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);
2016-01-05 10:51:13 -05:00
AppendSegment(node.location, PathData(0, node.name_id, extractor::TurnInstruction::HeadOn,
2016-01-04 04:19:25 -05:00
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);
2016-01-05 10:51:13 -05:00
const extractor::TravelMode travel_mode =
2016-01-04 04:19:25 -05:00
(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,
2016-01-07 19:31:57 -05:00
is_via_leg ? extractor::TurnInstruction::ReachViaLocation
: extractor::TurnInstruction::NoTurn,
2016-01-04 04:19:25 -05:00
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
2016-01-08 06:43:33 -05:00
if ((1 == segments.size()) && (segments.front().location == coordinate))
2016-01-04 04:19:25 -05:00
{
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
2016-01-05 10:51:13 -05:00
const auto getTurn = [](const PathData &path_point, const extractor::TravelMode previous_mode)
2016-01-04 04:19:25 -05:00
{
2016-01-08 06:43:33 -05:00
if (extractor::TurnInstruction::NoTurn == path_point.turn_instruction &&
previous_mode != path_point.travel_mode && path_point.segment_duration > 0)
2016-01-04 04:19:25 -05:00
{
2016-01-05 10:51:13 -05:00
return extractor::TurnInstruction::GoStraight;
2016-01-04 04:19:25 -05:00
}
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;
2016-01-05 10:51:13 -05:00
for (const auto i : util::irange<std::size_t>(1, segments.size()))
2016-01-04 04:19:25 -05:00
{
// move down names by one, q&d hack
segments[i - 1].name_id = segments[i].name_id;
2016-01-07 19:31:57 -05:00
segments[i].length = util::coordinate_calculation::greatCircleDistance(
segments[i - 1].location, segments[i].location);
2016-01-04 04:19:25 -05:00
}
float segment_length = 0.;
EdgeWeight segment_duration = 0;
std::size_t segment_start_index = 0;
double path_length = 0;
2016-01-05 10:51:13 -05:00
for (const auto i : util::irange<std::size_t>(1, segments.size()))
2016-01-04 04:19:25 -05:00
{
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;
2016-01-05 10:51:13 -05:00
if (extractor::TurnInstruction::NoTurn != segments[i].turn_instruction)
2016-01-04 04:19:25 -05:00
{
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;
2016-01-05 10:51:13 -05:00
segments.back().turn_instruction = extractor::TurnInstruction::NoTurn;
2016-01-04 04:19:25 -05:00
}
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.front().length &&
!(segments.begin() + 1)->is_via_location)
{
segments.erase(segments.begin());
2016-01-05 10:51:13 -05:00
segments.front().turn_instruction = extractor::TurnInstruction::HeadOn;
2016-01-04 04:19:25 -05:00
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 =
2016-01-05 10:51:13 -05:00
util::coordinate_calculation::bearing(first.location, second.location);
2016-01-04 04:19:25 -05:00
const double pre_turn_bearing =
2016-01-05 10:51:13 -05:00
util::coordinate_calculation::bearing(second.location, first.location);
2016-01-04 04:19:25 -05:00
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
2016-01-05 10:51:13 -05:00
util::for_each_pair(segments, markNecessarySegments);
2016-01-04 04:19:25 -05:00
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_