use enter + exit for roundabout instructions (#4358)
* Expose roundabout/rotary exit instructions as a new instruction type.
This commit is contained in:
committed by
Daniel Patterson
parent
0fc1aa2711
commit
c2dc7e9cd0
@@ -66,20 +66,20 @@ const constexpr TurnTypeName turn_type_names[] = {
|
||||
{"end of road", "end of road"},
|
||||
{"notification", "notification"},
|
||||
{"roundabout", "enter roundabout"},
|
||||
{"roundabout", "enter and exit roundabout"},
|
||||
{"exit roundabout", "enter and exit roundabout"},
|
||||
{"rotary", "enter rotary"},
|
||||
{"rotary", "enter and exit rotary"},
|
||||
{"exit rotary", "enter and exit rotary"},
|
||||
{"roundabout turn", "enter roundabout turn"},
|
||||
{"roundabout turn", "enter and exit roundabout turn"},
|
||||
{"use lane", "use lane"},
|
||||
{"invalid", "(noturn)"},
|
||||
{"invalid", "(suppressed)"},
|
||||
{"invalid", "(enter roundabout at exit)"},
|
||||
{"invalid", "(exit roundabout)"},
|
||||
{"invalid", "(enter rotary at exit)"},
|
||||
{"invalid", "(exit rotary)"},
|
||||
{"invalid", "(enter roundabout intersection at exit)"},
|
||||
{"invalid", "(exit roundabout intersection)"},
|
||||
{"roundabout", "roundabout"},
|
||||
{"exit roundabout", "exit roundabout"},
|
||||
{"rotary", "rotary"},
|
||||
{"exit rotary", "exit rotary"},
|
||||
{"roundabout turn", "roundabout turn"},
|
||||
{"exit roundabout", "exit roundabout turn"},
|
||||
{"invalid", "(stay on roundabout)"},
|
||||
{"invalid", "(sliproad)"}};
|
||||
|
||||
@@ -100,14 +100,14 @@ inline bool hasValidLanes(const guidance::IntermediateIntersection &intersection
|
||||
std::string instructionTypeToString(const TurnType::Enum type)
|
||||
{
|
||||
static_assert(sizeof(turn_type_names) / sizeof(turn_type_names[0]) >= TurnType::MaxTurnType,
|
||||
"Some turn types has not string representation.");
|
||||
"Some turn types have no string representation.");
|
||||
return turn_type_names[static_cast<std::size_t>(type)].external_name;
|
||||
}
|
||||
|
||||
std::string internalInstructionTypeToString(const TurnType::Enum type)
|
||||
{
|
||||
static_assert(sizeof(turn_type_names) / sizeof(turn_type_names[0]) >= TurnType::MaxTurnType,
|
||||
"Some turn types has not string representation.");
|
||||
"Some turn types have no string representation.");
|
||||
return turn_type_names[static_cast<std::size_t>(type)].internal_name;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "engine/guidance/collapsing_utility.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/group_by.hpp"
|
||||
#include "util/guidance/name_announcements.hpp"
|
||||
#include "util/guidance/turn_lanes.hpp"
|
||||
|
||||
@@ -14,6 +15,8 @@
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "engine/guidance/collapsing_utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
@@ -26,6 +29,8 @@ using osrm::extractor::guidance::hasRampType;
|
||||
using osrm::extractor::guidance::mirrorDirectionModifier;
|
||||
using osrm::extractor::guidance::bearingToDirectionModifier;
|
||||
|
||||
using RouteStepIterator = std::vector<osrm::engine::guidance::RouteStep>::iterator;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
@@ -36,208 +41,137 @@ namespace guidance
|
||||
namespace
|
||||
{
|
||||
|
||||
void fixFinalRoundabout(std::vector<RouteStep> &steps)
|
||||
// Ensure that after we are done with the roundabout, only the roundabout instructions themselves
|
||||
// remain
|
||||
void compressRange(const RouteStepIterator begin, const RouteStepIterator end)
|
||||
{
|
||||
for (std::size_t propagation_index = steps.size() - 1; propagation_index > 0;
|
||||
--propagation_index)
|
||||
{
|
||||
auto &propagation_step = steps[propagation_index];
|
||||
propagation_step.maneuver.exit = 0;
|
||||
if (entersRoundabout(propagation_step.maneuver.instruction))
|
||||
{
|
||||
// remember the current name as rotary name in tha case we end in a rotary
|
||||
if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
|
||||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
|
||||
{
|
||||
propagation_step.rotary_name = propagation_step.name;
|
||||
propagation_step.rotary_pronunciation = propagation_step.pronunciation;
|
||||
}
|
||||
else if (propagation_step.maneuver.instruction.type ==
|
||||
TurnType::EnterRoundaboutIntersection ||
|
||||
propagation_step.maneuver.instruction.type ==
|
||||
TurnType::EnterRoundaboutIntersectionAtExit)
|
||||
{
|
||||
propagation_step.maneuver.instruction.type = TurnType::EnterRoundabout;
|
||||
}
|
||||
if (begin == end)
|
||||
return;
|
||||
|
||||
return;
|
||||
}
|
||||
// accumulate turn data into the enter instructions
|
||||
else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout)
|
||||
for (auto itr = begin + 1; itr != end; ++itr)
|
||||
{
|
||||
// ensure not to invalidate the final arrive
|
||||
if (!hasWaypointType(*itr))
|
||||
{
|
||||
// TODO this operates on the data that is in the instructions.
|
||||
// We are missing out on the final segment after the last stay-on-roundabout
|
||||
// instruction though. it is not contained somewhere until now
|
||||
steps[propagation_index - 1].ElongateBy(propagation_step);
|
||||
steps[propagation_index - 1].maneuver.exit = propagation_step.maneuver.exit;
|
||||
propagation_step.Invalidate();
|
||||
begin->ElongateBy(*itr);
|
||||
itr->Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool setUpRoundabout(RouteStep &step)
|
||||
// this function handles a single roundabout between enter (which might be missing) to exit (which
|
||||
// might be missing as well)
|
||||
void processRoundaboutExits(const RouteStepIterator begin, const RouteStepIterator end)
|
||||
{
|
||||
// basic entry into a roundabout
|
||||
// Special case handling, if an entry is directly tied to an exit
|
||||
const auto instruction = step.maneuver.instruction;
|
||||
if (instruction.type == TurnType::EnterRotaryAtExit ||
|
||||
instruction.type == TurnType::EnterRoundaboutAtExit ||
|
||||
instruction.type == TurnType::EnterRoundaboutIntersectionAtExit)
|
||||
auto const last = end - 1;
|
||||
// If we do not exit the roundabout, there is no exit to report. All good here
|
||||
if (!leavesRoundabout(last->maneuver.instruction))
|
||||
{
|
||||
// Here we consider an actual entry, not an exit. We simply have to count the additional
|
||||
// exit
|
||||
step.maneuver.exit = 1;
|
||||
// prevent futher special case handling of these two.
|
||||
if (instruction.type == TurnType::EnterRotaryAtExit)
|
||||
step.maneuver.instruction.type = TurnType::EnterRotary;
|
||||
else if (instruction.type == TurnType::EnterRoundaboutAtExit)
|
||||
step.maneuver.instruction.type = TurnType::EnterRoundabout;
|
||||
else
|
||||
step.maneuver.instruction.type = TurnType::EnterRoundaboutIntersection;
|
||||
// first we do some clean-up
|
||||
if (begin->maneuver.instruction.type == TurnType::EnterRotary ||
|
||||
begin->maneuver.instruction.type == TurnType::EnterRotaryAtExit)
|
||||
{
|
||||
begin->rotary_name = begin->name;
|
||||
begin->rotary_pronunciation = begin->pronunciation;
|
||||
}
|
||||
// roundabout turns don't make sense without an exit, update the type
|
||||
else if (entersRoundabout(begin->maneuver.instruction) &&
|
||||
(begin->maneuver.instruction.type == TurnType::EnterRoundaboutIntersection ||
|
||||
begin->maneuver.instruction.type == TurnType::EnterRoundaboutIntersectionAtExit))
|
||||
{
|
||||
begin->maneuver.instruction.type = TurnType::EnterRoundabout;
|
||||
}
|
||||
|
||||
// We are doing a roundtrip on the roundabout, Nothing to do here but to remove the
|
||||
// instructions
|
||||
compressRange(begin, end);
|
||||
return;
|
||||
}
|
||||
|
||||
if (leavesRoundabout(instruction))
|
||||
{
|
||||
// This set-up, even though it looks the same, is actually looking at entering AND exiting
|
||||
step.maneuver.exit = 1; // count the otherwise missing exit
|
||||
const auto passes_exit_or_leaves_roundabout = [](auto const &step) {
|
||||
return staysOnRoundabout(step.maneuver.instruction) ||
|
||||
leavesRoundabout(step.maneuver.instruction);
|
||||
};
|
||||
|
||||
// prevent futher special case handling of these two.
|
||||
if (instruction.type == TurnType::EnterAndExitRotary)
|
||||
step.maneuver.instruction.type = TurnType::EnterRotary;
|
||||
else if (instruction.type == TurnType::EnterAndExitRoundabout)
|
||||
step.maneuver.instruction.type = TurnType::EnterRoundabout;
|
||||
else
|
||||
step.maneuver.instruction.type = TurnType::EnterRoundaboutIntersection;
|
||||
return false;
|
||||
// exit count
|
||||
const auto exit = std::count_if(begin, end, passes_exit_or_leaves_roundabout);
|
||||
|
||||
// removes all intermediate instructions, assigns names and exit numbers
|
||||
BOOST_ASSERT(leavesRoundabout(last->maneuver.instruction));
|
||||
BOOST_ASSERT(std::distance(begin, end) >= 1);
|
||||
last->maneuver.exit = exit;
|
||||
|
||||
// when we actually have an enter instruction, we can store all the information on it that we
|
||||
// need, otherwise we only provide the exit instruciton. In case of re-routing on the
|
||||
// roundabout, this might result in strange behaviour, but this way we are more resiliant and we
|
||||
// do provide exit after all
|
||||
if (entersRoundabout(begin->maneuver.instruction))
|
||||
{
|
||||
begin->maneuver.exit = exit;
|
||||
// special handling for rotaries: remember the name (legacy feature, due to
|
||||
// adapt-step-signage)
|
||||
if (begin->maneuver.instruction.type == TurnType::EnterRotary ||
|
||||
begin->maneuver.instruction.type == TurnType::EnterRotaryAtExit)
|
||||
{
|
||||
begin->rotary_name = begin->name;
|
||||
begin->rotary_pronunciation = begin->pronunciation;
|
||||
}
|
||||
// compute the total direction modifier for roundabout turns
|
||||
else if (begin->maneuver.instruction.type == TurnType::EnterRoundaboutIntersection ||
|
||||
begin->maneuver.instruction.type == TurnType::EnterRoundaboutIntersectionAtExit)
|
||||
{
|
||||
const auto entry_intersection = begin->intersections.front();
|
||||
|
||||
const auto exit_intersection = last->intersections.front();
|
||||
const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
||||
|
||||
BOOST_ASSERT(!begin->intersections.empty());
|
||||
const double angle = util::bearing::angleBetween(
|
||||
util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]),
|
||||
exit_bearing);
|
||||
|
||||
begin->maneuver.instruction.direction_modifier = getTurnDirection(angle);
|
||||
}
|
||||
begin->AdaptStepSignage(*last);
|
||||
}
|
||||
|
||||
// in case of a roundabout turn, we do not emit an exit as long as the mode remains the same
|
||||
if ((begin->maneuver.instruction.type == TurnType::EnterRoundaboutIntersection ||
|
||||
begin->maneuver.instruction.type == TurnType::EnterRoundaboutIntersectionAtExit) &&
|
||||
begin->mode == last->mode)
|
||||
{
|
||||
compressRange(begin, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
// do not remove last (the exit instruction)
|
||||
compressRange(begin, last);
|
||||
}
|
||||
}
|
||||
|
||||
void closeOffRoundabout(const bool on_roundabout,
|
||||
std::vector<RouteStep> &steps,
|
||||
std::size_t step_index)
|
||||
// roundabout groups are a sequence of roundabout instructions. This can contain enter/exit
|
||||
// instructions in between
|
||||
void processRoundaboutGroups(const std::pair<RouteStepIterator, RouteStepIterator> &range)
|
||||
{
|
||||
auto &step = steps[step_index];
|
||||
step.maneuver.exit += 1;
|
||||
if (!on_roundabout)
|
||||
const auto leaves_roundabout = [](auto const &step) {
|
||||
return leavesRoundabout(step.maneuver.instruction);
|
||||
};
|
||||
|
||||
auto itr = range.first;
|
||||
while (itr != range.second)
|
||||
{
|
||||
BOOST_ASSERT(steps.size() >= 2);
|
||||
|
||||
// We reached a special case that requires the addition of a special route step in the
|
||||
// beginning. We started in a roundabout, so to announce the exit, we move use the exit
|
||||
// instruction and move it right to the beginning to make sure to immediately announce the
|
||||
// exit.
|
||||
BOOST_ASSERT(leavesRoundabout(steps[1].maneuver.instruction) ||
|
||||
steps[1].maneuver.instruction.type == TurnType::StayOnRoundabout ||
|
||||
steps[1].maneuver.instruction.type == TurnType::Suppressed ||
|
||||
steps[1].maneuver.instruction.type == TurnType::NoTurn);
|
||||
steps[0].geometry_end = 1;
|
||||
steps[1].geometry_begin = 0;
|
||||
steps[1].AddInFront(steps[0]);
|
||||
steps[1].intersections.erase(steps[1].intersections.begin()); // otherwise we copy the
|
||||
// source
|
||||
if (leavesRoundabout(steps[1].maneuver.instruction))
|
||||
steps[1].maneuver.exit = 1;
|
||||
steps[0].duration = 0;
|
||||
steps[0].distance = 0;
|
||||
const auto exitToEnter = [](const TurnType::Enum type) {
|
||||
if (TurnType::ExitRotary == type)
|
||||
return TurnType::EnterRotary;
|
||||
// if we do not enter the roundabout Intersection, we cannot treat the full traversal as
|
||||
// a turn. So we switch it up to the roundabout type
|
||||
else if (type == TurnType::ExitRoundaboutIntersection)
|
||||
return TurnType::EnterRoundabout;
|
||||
else
|
||||
return TurnType::EnterRoundabout;
|
||||
};
|
||||
steps[1].maneuver.instruction.type = exitToEnter(step.maneuver.instruction.type);
|
||||
if (steps[1].maneuver.instruction.type == TurnType::EnterRotary)
|
||||
auto exit = std::find_if(itr, range.second, leaves_roundabout);
|
||||
if (exit == range.second)
|
||||
{
|
||||
steps[1].rotary_name = steps[0].name;
|
||||
steps[1].rotary_pronunciation = steps[0].pronunciation;
|
||||
processRoundaboutExits(itr, exit);
|
||||
itr = exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (step_index > 1)
|
||||
{
|
||||
auto &exit_step = steps[step_index];
|
||||
auto &prev_step = steps[step_index - 1];
|
||||
// In case the step with the roundabout exit instruction cannot be merged with the
|
||||
// previous step we change the instruction to a normal turn
|
||||
if (!guidance::haveSameMode(exit_step, prev_step))
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(leavesRoundabout(exit_step.maneuver.instruction));
|
||||
if (!entersRoundabout(prev_step.maneuver.instruction))
|
||||
{
|
||||
prev_step.maneuver.instruction = exit_step.maneuver.instruction;
|
||||
}
|
||||
prev_step.maneuver.exit = exit_step.maneuver.exit;
|
||||
exit_step.maneuver.instruction.type = TurnType::Notification;
|
||||
step_index--;
|
||||
processRoundaboutExits(itr, exit + 1);
|
||||
itr = exit + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the
|
||||
// index back to the entering location and prepare the current silent set of instructions for
|
||||
// removal.
|
||||
std::vector<std::size_t> intermediate_steps;
|
||||
BOOST_ASSERT(!steps[step_index].intersections.empty());
|
||||
// the very first intersection in the steps represents the location of the turn. Following
|
||||
// intersections are locations passed along the way
|
||||
const auto exit_intersection = steps[step_index].intersections.front();
|
||||
const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
||||
const auto destination_copy = step;
|
||||
|
||||
if (step_index > 1)
|
||||
{
|
||||
// The very first route-step is head, so we cannot iterate past that one
|
||||
for (std::size_t propagation_index = step_index - 1; propagation_index > 0;
|
||||
--propagation_index)
|
||||
{
|
||||
auto &propagation_step = steps[propagation_index];
|
||||
auto &next_step = steps[propagation_index + 1];
|
||||
if (guidance::haveSameMode(propagation_step, next_step))
|
||||
{
|
||||
propagation_step.ElongateBy(next_step);
|
||||
propagation_step.maneuver.exit = next_step.maneuver.exit;
|
||||
next_step.Invalidate();
|
||||
}
|
||||
|
||||
if (entersRoundabout(propagation_step.maneuver.instruction))
|
||||
{
|
||||
const auto entry_intersection = propagation_step.intersections.front();
|
||||
|
||||
// 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.rotary_pronunciation = propagation_step.pronunciation;
|
||||
}
|
||||
else if (propagation_step.maneuver.instruction.type ==
|
||||
TurnType::EnterRoundaboutIntersection ||
|
||||
propagation_step.maneuver.instruction.type ==
|
||||
TurnType::EnterRoundaboutIntersectionAtExit)
|
||||
{
|
||||
BOOST_ASSERT(!propagation_step.intersections.empty());
|
||||
const double angle = util::bearing::angleBetween(
|
||||
util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]),
|
||||
exit_bearing);
|
||||
|
||||
auto bearings = propagation_step.intersections.front().bearings;
|
||||
propagation_step.maneuver.instruction.direction_modifier =
|
||||
getTurnDirection(angle);
|
||||
}
|
||||
|
||||
propagation_step.AdaptStepSignage(destination_copy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// remove exit
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -248,64 +182,49 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
// They are required for maintenance purposes. We can calculate the number
|
||||
// of exits to pass in a roundabout and the number of intersections
|
||||
// that we come across.
|
||||
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
std::vector<RouteStep> handleRoundabouts(std::vector<RouteStep> steps)
|
||||
{
|
||||
// the steps should always include the first/last step in form of a location
|
||||
BOOST_ASSERT(steps.size() >= 2);
|
||||
if (steps.size() == 2)
|
||||
// check if a step has roundabout type
|
||||
const auto has_roundabout_type = [](auto const &step) {
|
||||
return hasRoundaboutType(step.maneuver.instruction);
|
||||
};
|
||||
const auto first_roundabout_type =
|
||||
std::find_if(steps.begin(), steps.end(), has_roundabout_type);
|
||||
|
||||
// no roundabout to process?
|
||||
if (first_roundabout_type == steps.end())
|
||||
return steps;
|
||||
|
||||
// Count Street Exits forward
|
||||
bool on_roundabout = false;
|
||||
bool has_entered_roundabout = false;
|
||||
// unless the first instruction enters the roundabout, we are currently on a roundabout. This is
|
||||
// a special case that happens if the route starts on a roundabout. It is a border case, but
|
||||
// could happen during re-routing. In the case of re-routing, exit counting might be confusing,
|
||||
// but it is the best we can do
|
||||
bool currently_on_roundabout = !entersRoundabout(first_roundabout_type->maneuver.instruction);
|
||||
|
||||
// count the exits forward. if enter/exit roundabout happen both, no further treatment is
|
||||
// required. We might end up with only one of them (e.g. starting within a roundabout)
|
||||
// or having a via-point in the roundabout.
|
||||
// In this case, exits are numbered from the start of the leg.
|
||||
for (std::size_t step_index = 0; step_index < steps.size(); ++step_index)
|
||||
{
|
||||
const auto next_step_index = step_index + 1;
|
||||
auto &step = steps[step_index];
|
||||
const auto instruction = step.maneuver.instruction;
|
||||
if (entersRoundabout(instruction))
|
||||
// this group by paradigm does might contain intermediate roundabout instructions, when they are
|
||||
// directly connected. Otherwise it will be a sequence containing everything from enter to exit.
|
||||
// If we already start on the roundabout, the first valid place will be steps.begin().
|
||||
const auto is_on_roundabout = [¤tly_on_roundabout](const auto &step) {
|
||||
if (currently_on_roundabout)
|
||||
{
|
||||
has_entered_roundabout = setUpRoundabout(step);
|
||||
if (leavesRoundabout(step.maneuver.instruction))
|
||||
currently_on_roundabout = false;
|
||||
|
||||
if (has_entered_roundabout && next_step_index < steps.size())
|
||||
steps[next_step_index].maneuver.exit = step.maneuver.exit;
|
||||
return true;
|
||||
}
|
||||
else if (instruction.type == TurnType::StayOnRoundabout)
|
||||
else
|
||||
{
|
||||
on_roundabout = true;
|
||||
// increase the exit number we require passing the exit
|
||||
step.maneuver.exit += 1;
|
||||
if (next_step_index < steps.size())
|
||||
steps[next_step_index].maneuver.exit = step.maneuver.exit;
|
||||
currently_on_roundabout = entersRoundabout(step.maneuver.instruction);
|
||||
auto result = currently_on_roundabout;
|
||||
// cases that immediately exit the roundabout
|
||||
if (currently_on_roundabout)
|
||||
currently_on_roundabout = !leavesRoundabout(step.maneuver.instruction);
|
||||
return result;
|
||||
}
|
||||
else if (leavesRoundabout(instruction))
|
||||
{
|
||||
// if (!has_entered_roundabout)
|
||||
// in case the we are not on a roundabout, the very first instruction
|
||||
// after the depart will be transformed into a roundabout and become
|
||||
// the first valid instruction
|
||||
closeOffRoundabout(has_entered_roundabout, steps, step_index);
|
||||
has_entered_roundabout = false;
|
||||
on_roundabout = false;
|
||||
}
|
||||
else if (on_roundabout && next_step_index < steps.size())
|
||||
{
|
||||
steps[next_step_index].maneuver.exit = step.maneuver.exit;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unterminated roundabout
|
||||
// Move backwards through the instructions until the start and remove the exit number
|
||||
// A roundabout without exit translates to enter-roundabout
|
||||
if (has_entered_roundabout || on_roundabout)
|
||||
{
|
||||
fixFinalRoundabout(steps);
|
||||
}
|
||||
// for each range of instructions between begin/end of a roundabout assign
|
||||
util::group_by(steps.begin(), steps.end(), is_on_roundabout, processRoundaboutGroups);
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||
@@ -333,8 +252,7 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||
return;
|
||||
|
||||
// if phantom node is located at the connection of two segments, either one can be selected
|
||||
// as
|
||||
// turn
|
||||
// as turn
|
||||
//
|
||||
// a --- b
|
||||
// |
|
||||
|
||||
Reference in New Issue
Block a user