adds distinction between rotaries/rounabouts

This commit is contained in:
Moritz Kobitzsch 2016-03-23 15:28:53 +01:00 committed by Patrick Niklaus
parent 278ec04f5e
commit f2443c64db
10 changed files with 270 additions and 42 deletions

View File

@ -44,6 +44,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const bool target_traversed_in_reverse) const bool target_traversed_in_reverse)
{ {
const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.; const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.;
const constexpr char* NO_ROTARY_NAME = "";
const EdgeWeight source_duration = const EdgeWeight source_duration =
source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight; source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight;
const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode
@ -86,6 +87,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const auto distance = leg_geometry.segment_distances[segment_index]; const auto distance = leg_geometry.segment_distances[segment_index];
steps.push_back(RouteStep{path_point.name_id, steps.push_back(RouteStep{path_point.name_id,
name, name,
NO_ROTARY_NAME,
segment_duration / 10.0, segment_duration / 10.0,
distance, distance,
path_point.travel_mode, path_point.travel_mode,
@ -103,6 +105,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
BOOST_ASSERT(duration >= 0); BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{target_node.name_id, steps.push_back(RouteStep{target_node.name_id,
facade.GetNameForID(target_node.name_id), facade.GetNameForID(target_node.name_id),
NO_ROTARY_NAME,
duration / 10., duration / 10.,
distance, distance,
target_mode, target_mode,
@ -126,6 +129,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
steps.push_back(RouteStep{source_node.name_id, steps.push_back(RouteStep{source_node.name_id,
facade.GetNameForID(source_node.name_id), facade.GetNameForID(source_node.name_id),
NO_ROTARY_NAME,
duration / 10., duration / 10.,
leg_geometry.segment_distances[segment_index], leg_geometry.segment_distances[segment_index],
source_mode, source_mode,
@ -140,6 +144,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry); extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry);
steps.push_back(RouteStep{target_node.name_id, steps.push_back(RouteStep{target_node.name_id,
facade.GetNameForID(target_node.name_id), facade.GetNameForID(target_node.name_id),
NO_ROTARY_NAME,
ZERO_DURATION, ZERO_DURATION,
ZERO_DISTANCE, ZERO_DISTANCE,
target_mode, target_mode,

View File

@ -25,6 +25,7 @@ struct RouteStep
{ {
unsigned name_id; unsigned name_id;
std::string name; std::string name;
std::string rotary_name;
double duration; double duration;
double distance; double distance;
extractor::TravelMode mode; extractor::TravelMode mode;

View File

@ -350,7 +350,8 @@ inline bool requiresNameAnnounced(const std::string &from, const std::string &to
const auto ref_begin = name.find_first_of('('); const auto ref_begin = name.find_first_of('(');
if (ref_begin != std::string::npos) if (ref_begin != std::string::npos)
{ {
out_name = name.substr(0, ref_begin); //we might need to trim a space, if there is a reference
out_name = name.substr(0, ref_begin - (ref_begin > 0 && name[ref_begin-1] == ' '));
out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1); out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1);
} }
else else

View File

@ -112,7 +112,8 @@ class TurnAnalysis
// Processing of roundabouts // Processing of roundabouts
// Produces instructions to enter/exit a roundabout or to stay on it. // Produces instructions to enter/exit a roundabout or to stay on it.
// Performs the distinction between roundabout and rotaries. // Performs the distinction between roundabout and rotaries.
std::vector<ConnectedRoad> handleRoundabouts(const EdgeID via_edge, std::vector<ConnectedRoad> handleRoundabouts(const bool is_rotary,
const EdgeID via_edge,
const bool on_roundabout, const bool on_roundabout,
const bool can_exit_roundabout, const bool can_exit_roundabout,
std::vector<ConnectedRoad> intersection) const; std::vector<ConnectedRoad> intersection) const;
@ -152,8 +153,8 @@ class TurnAnalysis
std::vector<ConnectedRoad> intersection) const; std::vector<ConnectedRoad> intersection) const;
// Any Junction containing motorways // Any Junction containing motorways
std::vector<ConnectedRoad> handleMotorwayJunction( std::vector<ConnectedRoad>
const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const; handleMotorwayJunction(const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge, std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const; std::vector<ConnectedRoad> intersection) const;
@ -193,6 +194,7 @@ class TurnAnalysis
std::vector<ConnectedRoad> intersection, std::vector<ConnectedRoad> intersection,
const std::size_t up_to) const; const std::size_t up_to) const;
bool isRotary(const NodeID nid) const;
}; // class TurnAnalysis }; // class TurnAnalysis
} // namespace guidance } // namespace guidance

View File

@ -89,24 +89,32 @@ struct TurnInstruction
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn); return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
} }
static TurnInstruction REMAIN_ROUNDABOUT(const DirectionModifier modifier) static TurnInstruction REMAIN_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
{ {
(void)is_rotary; // staying does not require a different instruction at the moment
return TurnInstruction(TurnType::StayOnRoundabout, modifier); return TurnInstruction(TurnType::StayOnRoundabout, modifier);
} }
static TurnInstruction ENTER_ROUNDABOUT(const DirectionModifier modifier) static TurnInstruction ENTER_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
{ {
return TurnInstruction(TurnType::EnterRoundabout, modifier); return {is_rotary ? TurnType::EnterRotary : TurnType::EnterRoundabout, modifier};
} }
static TurnInstruction EXIT_ROUNDABOUT(const DirectionModifier modifier) static TurnInstruction EXIT_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
{ {
return TurnInstruction(TurnType::ExitRoundabout, modifier); return {is_rotary ? TurnType::ExitRotary : TurnType::ExitRoundabout, modifier};
}
static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(bool is_rotary,
const DirectionModifier modifier)
{
return {is_rotary ? TurnType::EnterAndExitRotary : TurnType::EnterAndExitRoundabout,
modifier};
} }
static TurnInstruction SUPPRESSED(const DirectionModifier modifier) static TurnInstruction SUPPRESSED(const DirectionModifier modifier)
{ {
return TurnInstruction{TurnType::Suppressed, modifier}; return {TurnType::Suppressed, modifier};
} }
}; };

View File

@ -4,6 +4,7 @@
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include <boost/math/constants/constants.hpp> #include <boost/math/constants/constants.hpp>
#include <boost/optional.hpp>
#include <utility> #include <utility>
@ -61,6 +62,16 @@ double bearing(const Coordinate first_coordinate, const Coordinate second_coordi
// Get angle of line segment (A,C)->(C,B) // Get angle of line segment (A,C)->(C,B)
double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third); double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third);
// find the center of a circle through three coordinates
boost::optional<Coordinate> circleCenter(const Coordinate first_coordinate,
const Coordinate second_coordinate,
const Coordinate third_coordinate);
// find the radius of a circle through three coordinates
double circleRadius(const Coordinate first_coordinate,
const Coordinate second_coordinate,
const Coordinate third_coordinate);
// factor in [0,1]. Returns point along the straight line between from and to. 0 returns from, 1 // factor in [0,1]. Returns point along the straight line between from and to. 0 returns from, 1
// returns to // returns to
Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to); Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to);

View File

@ -1,16 +1,16 @@
#include "engine/api/json_factory.hpp" #include "engine/api/json_factory.hpp"
#include "engine/polyline_compressor.hpp"
#include "engine/hint.hpp" #include "engine/hint.hpp"
#include "engine/polyline_compressor.hpp"
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/range/irange.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/range/irange.hpp>
#include <string>
#include <utility>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <string>
#include <utility>
#include <vector> #include <vector>
using TurnType = osrm::extractor::guidance::TurnType; using TurnType = osrm::extractor::guidance::TurnType;
@ -28,14 +28,8 @@ namespace json
namespace detail namespace detail
{ {
const constexpr char *modifier_names[] = {"uturn", const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "slight right",
"sharp right", "straight", "slight left", "left", "sharp left"};
"right",
"slight right",
"straight",
"slight left",
"left",
"sharp left"};
// translations of TurnTypes. Not all types are exposed to the outside world. // translations of TurnTypes. Not all types are exposed to the outside world.
// invalid types should never be returned as part of the API // invalid types should never be returned as part of the API
@ -43,8 +37,8 @@ const constexpr char *turn_type_names[] = {
"invalid", "no turn", "invalid", "new name", "continue", "turn", "invalid", "no turn", "invalid", "new name", "continue", "turn",
"turn", "turn", "turn", "turn", "merge", "ramp", "turn", "turn", "turn", "turn", "merge", "ramp",
"ramp", "ramp", "ramp", "ramp", "fork", "end of road", "ramp", "ramp", "ramp", "ramp", "fork", "end of road",
"roundabout", "invalid", "roundabout", "invalid", "traffic circle", "invalid", "roundabout", "invalid", "roundabout", "invalid", "rotary", "invalid",
"traffic circle", "invalid", "invalid", "restriction", "notification"}; "rotary", "invalid", "invalid", "restriction", "notification"};
const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"}; const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
// Check whether to include a modifier in the result of the API // Check whether to include a modifier in the result of the API
@ -144,6 +138,7 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
if (detail::isValidModifier(maneuver)) if (detail::isValidModifier(maneuver))
step_maneuver.values["modifier"] = step_maneuver.values["modifier"] =
detail::instructionModifierToString(maneuver.instruction.direction_modifier); detail::instructionModifierToString(maneuver.instruction.direction_modifier);
step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location); step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location);
step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before); step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before);
step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after); step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after);
@ -164,6 +159,9 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo
route_step.values["distance"] = std::round(step.distance * 10) / 10.; route_step.values["distance"] = std::round(step.distance * 10) / 10.;
route_step.values["duration"] = std::round(step.duration * 10) / 10.; route_step.values["duration"] = std::round(step.duration * 10) / 10.;
route_step.values["name"] = std::move(step.name); route_step.values["name"] = std::move(step.name);
if (!step.rotary_name.empty())
route_step.values["rotary_name"] = std::move(step.rotary_name);
route_step.values["mode"] = detail::modeToString(std::move(step.mode)); route_step.values["mode"] = detail::modeToString(std::move(step.mode));
route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver)); route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver));
route_step.values["geometry"] = std::move(geometry); route_step.values["geometry"] = std::move(geometry);
@ -215,8 +213,7 @@ util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> legs,
json_steps.values.reserve(leg.steps.size()); json_steps.values.reserve(leg.steps.size());
std::transform( std::transform(
std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()), std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()),
std::back_inserter(json_steps.values), [&step_geometry_iter](guidance::RouteStep step) std::back_inserter(json_steps.values), [&step_geometry_iter](guidance::RouteStep step) {
{
return makeRouteStep(std::move(step), std::move(*step_geometry_iter++)); return makeRouteStep(std::move(step), std::move(*step_geometry_iter++));
}); });
json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps))); json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps)));

View File

@ -54,6 +54,11 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
{ {
propagation_step.maneuver.exit = 0; propagation_step.maneuver.exit = 0;
propagation_step.geometry_end = steps.back().geometry_begin; propagation_step.geometry_end = steps.back().geometry_begin;
if( propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit )
propagation_step.rotary_name = propagation_step.name;
break; break;
} }
else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout) else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout)
@ -80,18 +85,20 @@ bool setUpRoundabout(RouteStep &step)
step.maneuver.exit = 1; step.maneuver.exit = 1;
// prevent futher special case handling of these two. // prevent futher special case handling of these two.
if (instruction.type == TurnType::EnterRotaryAtExit) if (instruction.type == TurnType::EnterRotaryAtExit)
step.maneuver.instruction = TurnType::EnterRotary; step.maneuver.instruction.type = TurnType::EnterRotary;
else else
step.maneuver.instruction = TurnType::EnterRoundabout; step.maneuver.instruction.type = TurnType::EnterRoundabout;
} }
if (leavesRoundabout(instruction)) if (leavesRoundabout(instruction))
{ {
step.maneuver.exit = 1; // count the otherwise missing exit step.maneuver.exit = 1; // count the otherwise missing exit
if (instruction.type == TurnType::EnterRotaryAtExit)
step.maneuver.instruction = TurnType::EnterRotary; // prevent futher special case handling of these two.
if (instruction.type == TurnType::EnterAndExitRotary)
step.maneuver.instruction.type = TurnType::EnterRotary;
else else
step.maneuver.instruction = TurnType::EnterRoundabout; step.maneuver.instruction.type = TurnType::EnterRoundabout;
return false; return false;
} }
else else
@ -123,6 +130,8 @@ void closeOffRoundabout(const bool on_roundabout,
steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary
? TurnType::EnterRotary ? TurnType::EnterRotary
: TurnType::EnterRoundabout; : TurnType::EnterRoundabout;
if( steps[1].maneuver.instruction.type == TurnType::EnterRotary )
steps[1].rotary_name = steps[0].name;
} }
// Normal exit from the roundabout, or exit from a previously fixed roundabout. // Normal exit from the roundabout, or exit from a previously fixed roundabout.
@ -144,6 +153,11 @@ void closeOffRoundabout(const bool on_roundabout,
propagation_step.maneuver.exit = step.maneuver.exit; propagation_step.maneuver.exit = step.maneuver.exit;
propagation_step.geometry_end = step.geometry_end; propagation_step.geometry_end = step.geometry_end;
// remember rotary name
if( propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit )
propagation_step.rotary_name = propagation_step.name;
propagation_step.name = step.name; propagation_step.name = step.name;
propagation_step.name_id = step.name_id; propagation_step.name_id = step.name_id;
break; break;
@ -482,7 +496,7 @@ LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep
leg_geometry.segment_offsets.push_back(step.geometry_end - 1); leg_geometry.segment_offsets.push_back(step.geometry_end - 1);
} }
// remove the data fromt the reached-target step again // remove the data from the reached-target step again
leg_geometry.segment_offsets.pop_back(); leg_geometry.segment_offsets.pop_back();
leg_geometry.segment_distances.pop_back(); leg_geometry.segment_distances.pop_back();

View File

@ -2,10 +2,13 @@
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include <cstddef> #include <cstddef>
#include <limits> #include <limits>
#include <iomanip> #include <iomanip>
#include <set>
#include <unordered_set>
namespace osrm namespace osrm
{ {
@ -95,6 +98,111 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
} // namespace detail } // namespace detail
bool TurnAnalysis::isRotary(const NodeID nid) const
{
// translate a node ID into its respective coordinate stored in the node_info_list
const auto getCoordinate = [this](const NodeID node)
{
return util::Coordinate(node_info_list[node].lon, node_info_list[node].lat);
};
unsigned roundabout_name_id = 0;
std::unordered_set<unsigned> connected_names;
const auto getNextOnRoundabout =
[this, &roundabout_name_id, &connected_names](const NodeID node)
{
EdgeID continue_edge = SPECIAL_EDGEID;
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
{
const auto &edge_data = node_based_graph.GetEdgeData(edge);
if (!edge_data.reversed && edge_data.roundabout)
{
if (SPECIAL_EDGEID != continue_edge)
{
// fork in roundabout
return SPECIAL_EDGEID;
}
// roundabout does not keep its name
if (roundabout_name_id != 0 && roundabout_name_id != edge_data.name_id &&
requiresNameAnnounced(name_table.GetNameForID(roundabout_name_id),
name_table.GetNameForID(edge_data.name_id)))
{
return SPECIAL_EDGEID;
}
roundabout_name_id = edge_data.name_id;
continue_edge = edge;
}
else if (!edge_data.roundabout)
{
// remember all connected road names
connected_names.insert(edge_data.name_id);
}
}
return continue_edge;
};
// the roundabout radius has to be the same for all locations we look at it from
// to guarantee this, we search the full roundabout for its vertices
// and select the three smalles ids
std::set<NodeID> roundabout_nodes; // needs to be sorted
// this value is a hard abort to deal with potential self-loops
NodeID last_node = nid;
while (0 == roundabout_nodes.count(last_node))
{
roundabout_nodes.insert(last_node);
const auto eid = getNextOnRoundabout(last_node);
if (eid == SPECIAL_EDGEID)
{
util::SimpleLogger().Write(logDEBUG) << "Non-Loop Roundabout found.";
return false;
}
last_node = node_based_graph.GetTarget(eid);
}
// do we have a dedicated name for the rotary, if not its a roundabout
// This function can theoretically fail if the roundabout name is partly
// used with a reference and without. This will be fixed automatically
// when we handle references separately or if the useage is more consistent
if (roundabout_name_id == 0 || connected_names.count(roundabout_name_id))
{
return false;
}
if (roundabout_nodes.size() <= 1)
{
return false;
}
// calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal
// circle
// with both vertices right at the other side (so half their distance in meters).
// Otherwise, we construct a circle through the first tree vertices.
auto node_itr = roundabout_nodes.begin();
const double radius = roundabout_nodes.size() >= 3
? util::coordinate_calculation::circleRadius(
getCoordinate(*node_itr++),
getCoordinate(*node_itr++),
getCoordinate(*node_itr))
: 0.5 * util::coordinate_calculation::haversineDistance(
getCoordinate(*node_itr++),
getCoordinate(*node_itr));
// check whether the circle computation has gone wrong
// The radius computation can result in infinity, if the three coordinates are non-distinct.
// To stay on the safe side, we say its not a rotary
if (std::isinf(radius))
return false;
const double constexpr MAX_ROUNDABOUT_RADIUS = 15; // 30 m diameter as final distinction
return radius > MAX_ROUNDABOUT_RADIUS;
}
std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from, const EdgeID via_edge) const std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from, const EdgeID via_edge) const
{ {
localizer.node_info_list = &node_info_list; localizer.node_info_list = &node_info_list;
@ -132,7 +240,9 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from, const EdgeI
} }
if (on_roundabout || can_enter_roundabout) if (on_roundabout || can_enter_roundabout)
{ {
intersection = handleRoundabouts(via_edge, on_roundabout, can_exit_roundabout_separately, bool is_rotary = isRotary(node_based_graph.GetTarget(via_edge));
// find the radius of the roundabout
intersection = handleRoundabouts(is_rotary, via_edge, on_roundabout, can_exit_roundabout_separately,
std::move(intersection)); std::move(intersection));
} }
else else
@ -180,7 +290,8 @@ inline std::size_t countValid(const std::vector<ConnectedRoad> &intersection)
} }
std::vector<ConnectedRoad> std::vector<ConnectedRoad>
TurnAnalysis::handleRoundabouts(const EdgeID via_edge, TurnAnalysis::handleRoundabouts(const bool is_rotary,
const EdgeID via_edge,
const bool on_roundabout, const bool on_roundabout,
const bool can_exit_roundabout_separately, const bool can_exit_roundabout_separately,
std::vector<ConnectedRoad> intersection) const std::vector<ConnectedRoad> intersection) const
@ -207,12 +318,13 @@ TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
else else
{ {
turn.instruction = turn.instruction =
TurnInstruction::REMAIN_ROUNDABOUT(getTurnDirection(turn.angle)); TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
} }
} }
else else
{ {
turn.instruction = TurnInstruction::EXIT_ROUNDABOUT(getTurnDirection(turn.angle)); turn.instruction =
TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
} }
} }
return intersection; return intersection;
@ -227,7 +339,8 @@ TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
const auto &out_data = node_based_graph.GetEdgeData(turn.eid); const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
if (out_data.roundabout) if (out_data.roundabout)
{ {
turn.instruction = TurnInstruction::ENTER_ROUNDABOUT(getTurnDirection(turn.angle)); turn.instruction =
TurnInstruction::ENTER_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
if (can_exit_roundabout_separately) if (can_exit_roundabout_separately)
{ {
if (turn.instruction.type == TurnType::EnterRotary) if (turn.instruction.type == TurnType::EnterRotary)
@ -238,7 +351,8 @@ TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
} }
else else
{ {
turn.instruction = {TurnType::EnterAndExitRoundabout, getTurnDirection(turn.angle)}; turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT(
is_rotary, getTurnDirection(turn.angle));
} }
} }
return intersection; return intersection;
@ -1059,7 +1173,7 @@ void TurnAnalysis::handleDistinctConflict(const EdgeID via_edge,
} }
else else
{ {
// FIXME this should possibly know about the actual roads? // FIXME this should possibly know aboat the actual roads?
left.turn.instruction = getInstructionForObvious(4, via_edge, left); left.turn.instruction = getInstructionForObvious(4, via_edge, left);
right.turn.instruction = {findBasicTurnType(via_edge, right), right.turn.instruction = {findBasicTurnType(via_edge, right),
DirectionModifier::SlightRight}; DirectionModifier::SlightRight};

View File

@ -209,6 +209,81 @@ double computeAngle(const Coordinate first, const Coordinate second, const Coord
return angle; return angle;
} }
boost::optional<Coordinate>
circleCenter(const Coordinate C1, const Coordinate C2, const Coordinate C3)
{
// free after http://paulbourke.net/geometry/circlesphere/
// require three distinct points
if (C1 == C2 || C2 == C3 || C1 == C3)
return boost::none;
// define line through c1, c2 and c2,c3
const double C2C1_lat = static_cast<double>(toFloating(C2.lat - C1.lat)); // yDelta_a
const double C2C1_lon = static_cast<double>(toFloating(C2.lon - C1.lon)); // xDelta_a
const double C3C2_lat = static_cast<double>(toFloating(C3.lat - C2.lat)); // yDelta_b
const double C3C2_lon = static_cast<double>(toFloating(C3.lon - C2.lon)); // xDelta_b
// check for collinear points in X-Direction
if (std::abs(C2C1_lon) < std::numeric_limits<double>::epsilon() &&
std::abs(C3C2_lon) < std::numeric_limits<double>::epsilon())
{
return boost::none;
}
else if (std::abs(C2C1_lon) < std::numeric_limits<double>::epsilon())
{
// vertical line C2C1
// due to c1.lon == c2.lon && c1.lon != c3.lon we can rearrange this way
BOOST_ASSERT(std::abs(static_cast<double>(toFloating(C3.lon - C1.lon))) >=
std::numeric_limits<double>::epsilon() &&
std::abs(static_cast<double>(toFloating(C2.lon - C3.lon))) >=
std::numeric_limits<double>::epsilon());
return circleCenter(C1, C3, C2);
}
else if (std::abs(C3C2_lon) < std::numeric_limits<double>::epsilon())
{
// vertical line C3C2
// due to c2.lon == c3.lon && c1.lon != c3.lon we can rearrange this way
// after rearrangement both deltas will be zero
BOOST_ASSERT(std::abs(static_cast<double>(toFloating(C1.lon - C2.lon))) >=
std::numeric_limits<double>::epsilon() &&
std::abs(static_cast<double>(toFloating(C3.lon - C1.lon))) >=
std::numeric_limits<double>::epsilon());
return circleCenter(C2, C1, C3);
}
else
{ // valid slope values for both lines, calculate the center as intersection of the lines
const double C2C1_slope = C2C1_lat / C2C1_lon;
const double C3C2_slope = C3C2_lat / C3C2_lon;
//can this ever happen?
if (std::abs(C2C1_slope - C3C2_slope) < std::numeric_limits<double>::epsilon())
return boost::none;
const double C1_y = static_cast<double>(toFloating(C1.lat));
const double C1_x = static_cast<double>(toFloating(C1.lon));
const double C2_y = static_cast<double>(toFloating(C2.lat));
const double C2_x = static_cast<double>(toFloating(C2.lon));
const double C3_y = static_cast<double>(toFloating(C3.lat));
const double C3_x = static_cast<double>(toFloating(C3.lon));
const double lon = (C2C1_slope * C3C2_slope * (C1_y - C3_y) + C3C2_slope * (C1_x + C2_x) -
C2C1_slope * (C2_x + C3_x)) /
(2 * (C3C2_slope - C2C1_slope));
const double lat = (0.5 * (C1_x + C2_x) - lon) / C2C1_slope + 0.5 * (C1_y + C2_y);
return Coordinate(FloatLongitude(lon), FloatLatitude(lat));
}
}
double circleRadius(const Coordinate C1, const Coordinate C2, const Coordinate C3)
{
// a circle by three points requires thee distinct points
auto center = circleCenter(C1, C2, C3);
if (center)
return haversineDistance(C1, *center);
else
return std::numeric_limits<double>::infinity();
}
Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to) Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to)
{ {
BOOST_ASSERT(0 <= factor && factor <= 1.0); BOOST_ASSERT(0 <= factor && factor <= 1.0);