adds distinction between rotaries/rounabouts
This commit is contained in:
parent
278ec04f5e
commit
f2443c64db
@ -44,6 +44,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
const bool target_traversed_in_reverse)
|
||||
{
|
||||
const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.;
|
||||
const constexpr char* NO_ROTARY_NAME = "";
|
||||
const EdgeWeight source_duration =
|
||||
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
|
||||
@ -86,6 +87,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
const auto distance = leg_geometry.segment_distances[segment_index];
|
||||
steps.push_back(RouteStep{path_point.name_id,
|
||||
name,
|
||||
NO_ROTARY_NAME,
|
||||
segment_duration / 10.0,
|
||||
distance,
|
||||
path_point.travel_mode,
|
||||
@ -103,6 +105,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
BOOST_ASSERT(duration >= 0);
|
||||
steps.push_back(RouteStep{target_node.name_id,
|
||||
facade.GetNameForID(target_node.name_id),
|
||||
NO_ROTARY_NAME,
|
||||
duration / 10.,
|
||||
distance,
|
||||
target_mode,
|
||||
@ -126,6 +129,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
|
||||
steps.push_back(RouteStep{source_node.name_id,
|
||||
facade.GetNameForID(source_node.name_id),
|
||||
NO_ROTARY_NAME,
|
||||
duration / 10.,
|
||||
leg_geometry.segment_distances[segment_index],
|
||||
source_mode,
|
||||
@ -140,6 +144,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry);
|
||||
steps.push_back(RouteStep{target_node.name_id,
|
||||
facade.GetNameForID(target_node.name_id),
|
||||
NO_ROTARY_NAME,
|
||||
ZERO_DURATION,
|
||||
ZERO_DISTANCE,
|
||||
target_mode,
|
||||
|
@ -25,6 +25,7 @@ struct RouteStep
|
||||
{
|
||||
unsigned name_id;
|
||||
std::string name;
|
||||
std::string rotary_name;
|
||||
double duration;
|
||||
double distance;
|
||||
extractor::TravelMode mode;
|
||||
|
@ -350,7 +350,8 @@ inline bool requiresNameAnnounced(const std::string &from, const std::string &to
|
||||
const auto ref_begin = name.find_first_of('(');
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -112,7 +112,8 @@ class TurnAnalysis
|
||||
// Processing of roundabouts
|
||||
// Produces instructions to enter/exit a roundabout or to stay on it.
|
||||
// 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 can_exit_roundabout,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
@ -152,8 +153,8 @@ class TurnAnalysis
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Any Junction containing motorways
|
||||
std::vector<ConnectedRoad> handleMotorwayJunction(
|
||||
const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
|
||||
std::vector<ConnectedRoad>
|
||||
handleMotorwayJunction(const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
@ -193,6 +194,7 @@ class TurnAnalysis
|
||||
std::vector<ConnectedRoad> intersection,
|
||||
const std::size_t up_to) const;
|
||||
|
||||
bool isRotary(const NodeID nid) const;
|
||||
}; // class TurnAnalysis
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -89,24 +89,32 @@ struct TurnInstruction
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return TurnInstruction{TurnType::Suppressed, modifier};
|
||||
return {TurnType::Suppressed, modifier};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#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)
|
||||
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
|
||||
// returns to
|
||||
Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to);
|
||||
|
@ -1,16 +1,16 @@
|
||||
#include "engine/api/json_factory.hpp"
|
||||
|
||||
#include "engine/polyline_compressor.hpp"
|
||||
#include "engine/hint.hpp"
|
||||
#include "engine/polyline_compressor.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/range/irange.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using TurnType = osrm::extractor::guidance::TurnType;
|
||||
@ -28,23 +28,17 @@ namespace json
|
||||
namespace detail
|
||||
{
|
||||
|
||||
const constexpr char *modifier_names[] = {"uturn",
|
||||
"sharp right",
|
||||
"right",
|
||||
"slight right",
|
||||
"straight",
|
||||
"slight left",
|
||||
"left",
|
||||
"sharp left"};
|
||||
const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "slight right",
|
||||
"straight", "slight left", "left", "sharp left"};
|
||||
|
||||
// translations of TurnTypes. Not all types are exposed to the outside world.
|
||||
// invalid types should never be returned as part of the API
|
||||
const constexpr char *turn_type_names[] = {
|
||||
"invalid", "no turn", "invalid", "new name", "continue", "turn",
|
||||
"turn", "turn", "turn", "turn", "merge", "ramp",
|
||||
"ramp", "ramp", "ramp", "ramp", "fork", "end of road",
|
||||
"roundabout", "invalid", "roundabout", "invalid", "traffic circle", "invalid",
|
||||
"traffic circle", "invalid", "invalid", "restriction", "notification"};
|
||||
"invalid", "no turn", "invalid", "new name", "continue", "turn",
|
||||
"turn", "turn", "turn", "turn", "merge", "ramp",
|
||||
"ramp", "ramp", "ramp", "ramp", "fork", "end of road",
|
||||
"roundabout", "invalid", "roundabout", "invalid", "rotary", "invalid",
|
||||
"rotary", "invalid", "invalid", "restriction", "notification"};
|
||||
const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
|
||||
|
||||
// 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))
|
||||
step_maneuver.values["modifier"] =
|
||||
detail::instructionModifierToString(maneuver.instruction.direction_modifier);
|
||||
|
||||
step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location);
|
||||
step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before);
|
||||
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["duration"] = std::round(step.duration * 10) / 10.;
|
||||
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["maneuver"] = makeStepManeuver(std::move(step.maneuver));
|
||||
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());
|
||||
std::transform(
|
||||
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++));
|
||||
});
|
||||
json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps)));
|
||||
|
@ -54,6 +54,11 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
|
||||
{
|
||||
propagation_step.maneuver.exit = 0;
|
||||
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;
|
||||
}
|
||||
else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout)
|
||||
@ -80,18 +85,20 @@ bool setUpRoundabout(RouteStep &step)
|
||||
step.maneuver.exit = 1;
|
||||
// prevent futher special case handling of these two.
|
||||
if (instruction.type == TurnType::EnterRotaryAtExit)
|
||||
step.maneuver.instruction = TurnType::EnterRotary;
|
||||
step.maneuver.instruction.type = TurnType::EnterRotary;
|
||||
else
|
||||
step.maneuver.instruction = TurnType::EnterRoundabout;
|
||||
step.maneuver.instruction.type = TurnType::EnterRoundabout;
|
||||
}
|
||||
|
||||
if (leavesRoundabout(instruction))
|
||||
{
|
||||
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
|
||||
step.maneuver.instruction = TurnType::EnterRoundabout;
|
||||
step.maneuver.instruction.type = TurnType::EnterRoundabout;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@ -123,6 +130,8 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary
|
||||
? TurnType::EnterRotary
|
||||
: 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.
|
||||
@ -144,6 +153,11 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
|
||||
propagation_step.maneuver.exit = step.maneuver.exit;
|
||||
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_id = step.name_id;
|
||||
break;
|
||||
@ -482,7 +496,7 @@ LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep
|
||||
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_distances.pop_back();
|
||||
|
||||
|
@ -2,10 +2,13 @@
|
||||
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@ -95,6 +98,111 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
|
||||
|
||||
} // 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
else
|
||||
@ -180,7 +290,8 @@ inline std::size_t countValid(const std::vector<ConnectedRoad> &intersection)
|
||||
}
|
||||
|
||||
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 can_exit_roundabout_separately,
|
||||
std::vector<ConnectedRoad> intersection) const
|
||||
@ -207,12 +318,13 @@ TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
|
||||
else
|
||||
{
|
||||
turn.instruction =
|
||||
TurnInstruction::REMAIN_ROUNDABOUT(getTurnDirection(turn.angle));
|
||||
TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
turn.instruction = TurnInstruction::EXIT_ROUNDABOUT(getTurnDirection(turn.angle));
|
||||
turn.instruction =
|
||||
TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
@ -227,7 +339,8 @@ TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
|
||||
const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
|
||||
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 (turn.instruction.type == TurnType::EnterRotary)
|
||||
@ -238,7 +351,8 @@ TurnAnalysis::handleRoundabouts(const EdgeID via_edge,
|
||||
}
|
||||
else
|
||||
{
|
||||
turn.instruction = {TurnType::EnterAndExitRoundabout, getTurnDirection(turn.angle)};
|
||||
turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT(
|
||||
is_rotary, getTurnDirection(turn.angle));
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
@ -1059,7 +1173,7 @@ void TurnAnalysis::handleDistinctConflict(const EdgeID via_edge,
|
||||
}
|
||||
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);
|
||||
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
||||
DirectionModifier::SlightRight};
|
||||
|
@ -209,6 +209,81 @@ double computeAngle(const Coordinate first, const Coordinate second, const Coord
|
||||
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)
|
||||
{
|
||||
BOOST_ASSERT(0 <= factor && factor <= 1.0);
|
||||
|
Loading…
Reference in New Issue
Block a user